shuoj 小6的多公尺諾骨牌 雙向dp

2021-07-04 07:46:37 字數 3835 閱讀 2838

小6

有一副多公尺諾骨牌,它們的高度不一,且不計厚度。

小6將這些骨牌從左到右排成一排立起來,如果向左或者向右推倒其中乙個骨牌,那麼它碰到左邊或者右邊的骨牌會一起連續倒下。

也就是說,每個骨牌只能向左或者向右倒下。

小6想知道,最少需要直接推倒多少骨牌,才能把所有的骨牌全都放倒。

第一行是乙個整數t

,表示資料組數。(t≤

20)

每組輸入中的第一行為多公尺諾骨牌數量n(1≤n≤100),

接著有n行,第i行有兩個正整數pi、hi分別代表多公尺諾骨牌的橫座標和該骨牌的高度,資料保證每個座標最多只有乙個骨牌,並且所給出的pi是遞增的。

(1≤pi, hi

≤1000)

對於每組輸入,在一行中輸出乙個整數,表示要把所有骨牌直接或間接放倒最少需要直接推倒的骨牌數量。2

3 1 3

4 2

5 2 3

1 3

4 3

7 72 1

題解::自認為這道題的解法很難想明白,從第一次見到這個題也有幾個月了。直到前幾天看到題解才大概有了一點感覺,今天想這道題想了很久,也演示了幾遍,終於搞清楚了一點。這道題的思路是這樣的:

(這裡輸入骨牌從0開始,dp從1開始)用dp[ i ]記錄第 i 個骨牌最少能被幾次推到,這樣dp[ n ]就是要求的結果。更新的原則是: 從左向右遍歷一遍骨牌,先從左向右更新當前骨牌能夠放倒的所有骨牌,然後從右更新(更新的骨牌為能夠從右向左砸到當前骨牌的牌以及能夠砸到所有能被更新的骨牌的所有骨牌)。在這裡舉個例子(1,3)(2,4)(5,2)(7,6)(10,1)(11,3)當遍歷第乙個節點(1,3)的時候從左更新會更新(1,3)(2,4)(5,2)【這裡不解釋】,從右更新會更新(1,3)(2,4)(7,6),【這裡第二張(2,4)能夠砸倒(1,3)更新第二張,(7,6)能夠砸倒(2,4)更新第三張,(10,1)不能砸倒(7,6)不更新第四張,就是這樣的原則。

首先要把dp[ ] 初始化為無窮大inf,在從左向右方向的過程中狀態轉移方程為dp[ j +1]  =  min(dp[ j +1] , dp[ i ]+1) , 其中j為要更新的牌,i為當前遍歷到的第幾張。在p[j]+在從右向左方向的過程中狀態轉移方程為dp[ j +1] = min(dp[ j +1],dp[ i ]+1);

這裡給出一組樣例,以及輸出,看了程式後跟著程式一步一步的走就明白意思了。

************標頭檔案***************

********************************/

int p[110];

int h[110];

int dp[110];

int main() {

ios::sync_with_stdio(false);

int t;

cin >> t;

while (t--) {

int n;

cin >> n;

clr(dp,inf);

rep(i, n) cin >> p[i] >> h[i];

dp[0] = 0;

rep(i, n) {

//首先從左向右倒

int r = p[i] + 1;//要確保在跟自己進行比較的時候一定會把自己推到

//這裡要註明下面的p[x]表示第x+1塊多公尺諾牌,dp[x]表示第x塊多公尺諾牌最少第幾次被推倒

rep2(j, i, n-1) {

if (p[j] < r) {//說明可以推到;p[j]和下面的dp[j+1]指的是一塊牌

dp[j+1] = min(dp[j+1], dp[i]+1);

//這裡應用的很巧妙,當i==j是必然是可以進入迴圈的

//如果dp[j+1]沒有被更新過,說明以前不會被推倒,它被推倒的次數最小值就會比上一張牌多一,

//但是如果被更新過,說明上一張牌可以把這一張砸到,那麼dp[i]==dp[j+1],所以最小值仍為dp[j+1]

//當j>i的時候要進入迴圈必然是能被砸到的,這時候dp[j+1]肯定應該等於dp[j]

//這裡很難想明白,可以把下面這句話輸出來,很有助於理解

//cout<

這道題暫時告一段落,還需要繼續學習……

2015/10/10

今天終於又看到這道題,感覺理解深刻了很多。dp的作用就是將複雜過程分解成簡單的步驟地推出來。貼出新的**,雖然沒有太大的改進,但是思路變得簡單了(就是原來太麻煩了……)

#includeusing namespace std;

#define inf 0x3f3f3f3f

const int maxn = 110;

int p[maxn];

int h[maxn];

int dp[maxn];

int main(){

int t;

cin>>t;

while(t--){

int n;

cin>>n;

memset(dp,inf,sizeof(dp));

/**記錄節點從 1開始,思考更方便(統一dp和p,h的索引)*/

for(int i = 1;i<=n;i++) cin>>p[i]>>h[i];

/**初始化dp[0]是為dp[1]服務*/

dp[0] = 0;

for(int i = 1;i<=n;i++){

/*r和l的作用是不一樣的,向左和向右的思路是不一樣的。因為向右為正序,更新後的r

為向右推倒的最大位置。向左為逆序,l表示當前要被推到的位置。所以在向右的時候else

要break,因為再進行下去已經沒有意義了。而向左則不一樣,即使位置較大的p也可能推到當前的位置。

這樣就有乙個弊端,dp[i]並不一定為推到i的最小次數(好像也沒什麼卵用)不用管**/

int r = p[i]+h[i];

for(int j = i;j<=n;j++){

if(p[j]

多公尺諾骨牌

100張多公尺諾骨牌整齊地排成一列,按順序編號為1 2 3 4 99 100。第一次拿走所有的奇數字置上的骨牌,第二次再從剩餘的骨牌中拿走所有奇數字置上的骨牌,依次類推,請問最後剩下的一張骨牌的編號是多少 a.48 b.50 c.52 d.64 正確答案 d.答對了嗎?答對了嗎?答對了嗎?第一次拿走...

多公尺諾骨牌

現有n塊 多公尺諾骨牌 s1,s2,s3,sn水平放成一排,每次骨牌si包含左右兩個部分,每個部分賦予乙個非負整數值,如下圖所示為包含6塊骨牌的序列.骨牌可做180度旋轉,使得原來在左邊的值變到右邊,而原來右邊的值移到左邊,假設不論si如何旋轉,l i 總是儲存si左邊的值,r i 總是儲存si右邊...

多公尺諾骨牌

在課堂上,我們分析了棋盤覆蓋問題,同學們也看了我的程式執行的情況,今天我們來看一下另外乙個覆蓋問題。今天的問題是這樣的 用n個2x1的矩形 這種矩形我們以後稱之為骨牌或多公尺諾 覆蓋2xn的棋盤,有多少種不同的覆蓋法?input 本問題有多組測試資料,對於每一組測試資料,輸入只有一行n 意義如上所述...