1. 程式人生 > >NOIP 2008 傳紙條(洛谷P1006,動態規劃遞推,滾動陣列)

NOIP 2008 傳紙條(洛谷P1006,動態規劃遞推,滾動陣列)

題目連結:P1006 傳紙條

 

PS:傷心,又想不出來,看了大神的題解

 

AC程式碼:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,f[210][210],a[210][210];
int main()
{
    ll i,j,k;
    cin>>n>>m;
    for (i=1;i<=n;++i)
        for (j=1;j<=m;++j)
            cin
>>a[i][j]; f[1][2]=a[1][2]+a[2][1];//不管怎樣都會經過(1,2)和(2,1)先賦初值 //i是當前的點橫縱座標之和 //下面的迴圈是起碼2*3或者3*2棋盤才會進入,如果2*2及以下棋盤已經初定義了 for (i=4;i<n+m;++i)//至於i<n+m我是畫草圖才理解的 { for (j=min(i-2,n);j>=1;j--) //滾動陣列,i層和i-層有關,所以j,k要一直減小而不是增大,防止資料覆蓋 {//j=min(i-2,n)是在不超出的情況下找儘量小的能走的位置
for (k=min(i-1,n);k>j;k--) {//k在j右邊,所以是i-1 if (j>1) //這裡的條件判斷貌似是不需要的,但加上更好 { f[j][k]=max(f[j][k],f[j-1][k]); } if (j>1&&k>1) { f[j][k]
=max(f[j][k],f[j-1][k-1]); } if (k-1>j)//保證移動前的點沒有重合 { f[j][k]=max(f[j][k],f[j][k-1]); } //對於兩個都從上邊來的,其實是另一對(j,k)的兩個都從左邊來,所以不用寫了 f[j][k]+=a[j][i-j]+a[k][i-k];//把數字帶上 } } } cout<<f[n-1][n];//輸出左下角左邊和上邊兩個格的數字和就好了 return 0; }
點選加號展開程式碼

 

談談我對這個思路的理解:

1.我們可以看成兩紙條同時從左上角原點出發

2.我們發現對於一個畫一條條斜線,斜線上的每個點橫座標加縱座標的和都相同

 

而且原點到同一條線上任意點要走的距離相同,那麼每走一步,紙條a和紙條b都在一條斜線上

為了防止相遇,我們可以假設b的橫座標k永遠大於a的橫座標j

而且,我們用i=n+m,那麼任意一點的橫座標=i-縱座標,縱座標同理,也就是橫座標+縱座標=i=n+m

3.那我們用i的變化來表示他們現在走到哪條斜線了,

這樣一來,就可以開始遞推,而且i只和i-1有關係,這是用滾動陣列的前提

4.對於陣列f[j,k],我們可以看成a走到(j,i-j),b走到(k,i-k)時撿起的總數字

5.然後賦初值,一開始第一步肯定是一個向右,一個向下,所以:

 f[1][2]=a[1][2]+a[2][1];

不管怎樣都會經過(1,2)和(2,1)先賦初值

6.迴圈如何實現

for (i=4;i<n+m;++i)//至於i<n+m我是畫草圖才理解的
    for (j=min(i-2,n);j>=1;j--) 
//滾動陣列,i層和i-層有關,所以j,k要一直減小而不是增大,防止資料覆蓋
//j=min(i-2,n)是在不超出的情況下找儘量小的能走的位置
          for (k=min(i-1,n);k>j;k--)//k在j右邊,所以是i-1

第一維for表示要遞推的次數,從4開始是因為如果n,m為1,3就沒意義了,而m,n為2,2就已經靠初始化就夠了,不需要遞推

第二維是考慮紙條a的遞推,從大到小是因為每次都要用前面的資料賦值新的資料,所以這樣能防止覆蓋

第三維和第二維差不多

7.遞推式

if (j>1)  //這裡的條件判斷貌似是不需要的,但加上更好
{
f[j][k]=max(f[j][k],f[j-1][k]);
}
 if (j>1&&k>1)
{
 f[j][k]=max(f[j][k],f[j-1][k-1]);
}
 if (k-1>j)//保證移動前的點沒有重合
{
 f[j][k]=max(f[j][k],f[j][k-1]);
}
  //對於兩個都從上邊來的,其實是另一對(j,k)的兩個都從左邊來,所以不用寫了

解釋以下,這裡分別代表

a從左來,b從左來

a從上來,b從左來

a從左來,b從上來

有人回問那兩個都從上來去哪了?額,它其實在另外一個地方被算過了,下面上圖解釋

紅色和藍色表示兩個都從上面來,其實他們就等於黃色的那兩個地方的兩個都從左邊來

 

還有一個地方要注意的是:a從上來,b從左來,這時候要k-1>j,保證移動前的兩個點不是重合的

下面上個圖解釋一下,下圖是錯誤情況

8.輸出結果,只要輸出當a,b到最右下角那個點的左邊和上面的情況的值就好了

 

老是寫不出來DP,自閉了...