牛客網 資料結構筆試題目(五) 動態規劃問題求解

2021-10-21 19:07:58 字數 3301 閱讀 7292

給定n個整數,對於這n個整數我們可以採取兩種操作。第一種操作是在陣列左側選擇連續的k個整數減1,第二種操作是選擇右側的連續k個整數減1。

比如假設陣列是[3, 2, 2, 1, 4],比如我們選擇k=2,取最左側進行操作,那麼我們會得到[2, 1, 2, 1, 4]。如果我們選擇k=3,再取右側進行操作,可以得到[2, 1, 1, 0, 3]。

現在我們想要知道,給定這樣的陣列,我們能否通過這兩個操作將陣列清空。如果可以輸出yes,否則的話輸出no。

首先輸入乙個整數t(),表示測試資料組數。

對於每組測試資料,首先輸入乙個整數n(),表示陣列當中元素的個數。之後輸入一行整數()。可以保證,每一組測試資料的n之和不會超過30000.

由於我們對於k沒有限制,最多我們可以一次對陣列內的n個元素全部減一。所以k不是限制我們的因素,最大的限制其實是在元素本身。

我們分析一下會發現,由於陣列當中的元素大小不一,這其實是**的限制。舉個例子,比如[2, 8, 3]。由於我們只能從兩側開始選擇元素進行操作,所以由於2和3比較小,會導致我們沒有辦法把中間的8消除完。當然無法消除的原因可能有好幾種,但基本上都是由於元素的大小不一導致的。

首先我們對這個問題進行乙個簡單的建模,題目當中沒有限制執行的次數,所以減一次和減很多次是一樣的。我們可以把可以合併的操作合併在一起,理解成執行一次可以減去任意的值。並且我們可以把操作反向理解,把陣列當中的值看成是容器,這樣我們從陣列當中減去值的操作,就可以等價理解成向容器當中輸入水流,這樣會容易理解一些。

我的第一想法很簡單,我們可以求出每個位置能夠從左側和右側分別獲得的最大數值。只要左右兩側能夠獲取的流量之和大於等於容器的容積,那麼就說明我們可以獲取到足夠的流量灌滿所有的容器。

我很快就寫出了**,建了乙個二維陣列,dp[i][0]表示第i個元素從左側源頭能夠獲取的最大流量。dp[i][1]表示第i個元素可以從右側源頭獲取到的最大流量。由於我們需要保證每個容器儲存的體積不能超過容量,所以我們需要很容易得出遞推關係。

dp[i][0] = min(dp[i-1][0], a[i])

關係明確了很容易寫出**:

using namespace std;

int a[

30006

], dp[

30006][

2];int

main()

// 從右側遞推,獲取dp[i][1]

rep(i, n,0)

bool flag =1;

rep(i,

1, n+1)

}puts

(flag ?

"yes"

:"no");

}return0;

}

但是很遺憾,這樣不能ac,因為dp的陣列維護的其實是某個位置從左側和從右側能夠獲取的最大值,這是乙個理想情況,很有可能這個理想情況是無法實現的。

舉個很簡單的反例:[2, 4, 2, 4, 2],這些元素左右兩邊能夠獲取到的最大流量值都是2,但是這裡是有問題的。觀察一下會發現陣列當中的兩個4是無法同時滿足的,無法滿足的原因是因為中間的2限制了通過的流量。雖然理論上從左往右和從右往左能夠通過的流量上限都是2,但是這個上限是無法同時取到的。

這個問題用上述的方法是解決不了的,所以需要重新構思。這裡我們深入分析會發現乙個比較麻煩的點,在於每個點都有兩個源頭,我們無法確定流量分配。不過這個問題也很好解決,因為左右兩邊的流量是沒有區別的。所以我們可以以某一側為主,剩餘不夠的流量再由另一側補充。

比如我們可以以左側為主,把左側能夠獲取的流量開啟到最大,不夠地再通過右側補充。如果右側的流量無法補充,那麼就說明無解。

我們用dp[i][0]記錄i位置從左側獲取的流量,dp[i][1]記錄i位置從右側獲取的流量。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

"time.h"

#include

#define rep(i,a,b) for (int i=a;i#define rep(i,a,b) for (int i=a;i>b;i--)

#define foreach(e,x) for (__typeof(x.begin()) e=x.begin();e!=x.end();e++)

#define mid ((l+r)>>1)

#define lson (k<<1)

#define rson (k<<1|1)

#define mem(a,x) memset(a,x,sizeof a)

#define l ch[r][0]

#define r ch[r][1]

const

int n=

1000050

;const

long

long mod=

1000000007

;using

namespace std;

int a[

30006

], dp[

30006][

2], min_need[

30006][

2], record[

30006][

2];int

main()

# 左側能夠獲取的流量,因為i-

1從右側獲取的流量也會經過i,所以需要減去

dp[i][0

]=min(dp[i-1]

[0], a[i]

- dp[i-1]

[1])

; # 需要從右側獲取的流量需要累加

dp[i][1

]= dp[i-1]

[1]+

max(

0, a[i]

- dp[i][0

]- dp[i-1]

[1])

;}puts

(flag ?

"yes"

:"no");

}return0;

}

雖然這個是很簡單的動態規劃的思想,但是一些細節很容易忽略。比如說i-1位置的右側流量會流經i以及大於i每乙個位置。所以每乙個位置的右側流量是累加的,是越來越大的。只要能夠把握住這點,ac是不難的。

總體來說這題的難度不大,對於思維的要求不是很高,但是非常考驗思維的縝密性和邏輯性。非常適合用來進行思維鍛鍊。

筆試題目 牛客網輸入規範

方法一 單行輸入規範 import sys for line in sys.stdin a line.split print a 列印出列表a print int a 0 break 輸入一行就跳出迴圈方法二 python2與python3 通用 import sys num sys.stdin.r...

資料結構筆試題目

1 c 編成求二叉樹的深度 int bintreedepth link head 2 排序二叉樹插入乙個節點或雙向鍊錶的實現 排序二叉樹 左小於根,根小於右。左右又分別是排序二叉樹。前序遍歷 根左右 中序遍歷 左根右 後序遍歷 左右根 排序二叉樹插入乙個結點 大於左,往右找,小於右往左找,遞迴實現。...

牛客網 簡單的資料結構

慄醬有一天在網上衝浪的時候發現了一道很有意思的資料結構題。該資料結構形如長條形。一開始該容器為空,有以下七種操作。1 a從前面插入元素a 2 從前面刪除乙個元素 3 a從後面插入乙個元素 4 從後面刪除乙個元素 5 將整個容器頭尾翻轉 6 輸出個數和所有元素 7 對所有元素進行從小到大排序 只有一組...