1. 程式人生 > 其它 >【題解】 [CSP-J2020] 方格取數

【題解】 [CSP-J2020] 方格取數

置頂:一篇超級好的題解,講得非常細緻

#include<iostream>
using namespace std;

const long long inflw = -1e17;
long long n,m;
long long mapn[1019][1019];
long long dp[1019][1019][5];

void init(){//初始化
    for(int i=0; i<1009; i++)
        for(int j=0; j<1009; j++)
            for(int k=0; k<5; k++)
                dp[i][j][k] = inflw;//要是負無窮
}

int main(){
    init();//初始化
    scanf("%lld %lld",&n,&m);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            scanf("%lld",&mapn[i][j]);

    dp[1][1][0] = dp[1][1][1] = dp[1][1][2] = mapn[1][1];
    for(int i=2; i<=n; i++){
        dp[i][1][0] = dp[i-1][1][0]+mapn[i][1];
        //從右邊,下邊走不到(i,1),只能從上邊走來
    }
    for(int i=2; i<=m; i++){
        for(int j=1; j<=n; j++){

            //從(j,i)的上邊來到(j,i)  ↓
            if(j >= 2)//只有當 j≥2 時才有“上”
                dp[j][i][0] = max(dp[j-1][i][0],dp[j-1][i][1])+mapn[j][i];

            //從(j,i)的左邊來到(j-i)  →
                dp[j][i][1] = max(dp[j][i-1][1],max(dp[j][i-1][0],dp[j][i-1][2]))+mapn[j][i];
        
        }
        //從(j,i)的下邊來到(j,i)
        for(int j=n-1; j>=1; j--){//只有當 j=n-1 時才有“下”
            dp[j][i][2] = max(dp[j+1][i][1],dp[j+1][i][2])+mapn[j][i];
        }
    }
    printf("%lld",max(dp[n][m][0],max(dp[n][m][1],dp[n][m][2])));//
    return 0;
}

狀態: $dp[x][y][0/1/2]$ : 從 $(1,1)$ 出發,到達 $(x,y)$,每個點只走一次(沒有重複),從上方/右方/下方,最大權值和 0 : 上 1 : 右 2 : 下 坑: 1. 在處理 $dp[j][i][2]$ 的時候,應該寫成
dp[j][i][2] = max(dp[j+1][i][1],dp[j+1][i][2])+mapn[j][i];
而不是
dp[j][i][2] = max(dp[j+1][i][1],max(dp[j+1][i][2],dp[j+1][i][0]))+mapn[j][i];
就是說到一個從 $(j+1,i)$ 到 $(j,i)$ 時,要取 $(j+1,i)$ 的上,右兩個狀態的最大值,因為下的那個狀態和 $dp_{j,i}$ 衝突了。 2. 同理,在 $j$ ≥ $2$ 那個 if 語句裡,和第一條一樣。
對於一個狀態來說,他只和他前面(已知)的狀態有關(他是從他前面的狀態得來的)!