1. 程式人生 > 實用技巧 >2020.7.27考試D1T1:Cow Pie Treasures

2020.7.27考試D1T1:Cow Pie Treasures

原題目:
知乎回答:DP水題賽爆零是一種怎樣的體驗?

D1T1:Cow Pie Treasures

題目傳送門

Description
最近,奶牛們熱衷於把金幣包在麵粉裡,然後把它們烤成餡餅。第i塊餡餅中含有Ni(1<=Ni<=25)塊金幣,並且,這個數字被醒目地標記在餡餅表面。 奶牛們把所有烤好的餡餅在草地上排成了一個R行(1<=R<=100)C列(1<=C<=100)的矩陣。你現在站在座標為(1,1)的餡餅邊上,當然,你可以拿到那塊餡餅裡的所有金幣。你必須從現在的位置,走到草地的另一邊,在座標為(R,C)的餡餅旁邊停止走動。每做一次移動,你必須走到下一列的某塊餡餅旁邊,並且,行數的變動不能超過1(也就是說,如果現在你站在座標為(r,c)的餡餅邊上,下一步你可以走到座標為(r-1,c+1),(r,c+1),或者(r+1,c+1)的餡餅旁邊)。當你從一塊餡餅邊經過,你就可以拿走餡餅裡所有的金幣。當然啦,你一定不會願意因半路離開草地而失去唾手可得的金幣,但,最終你一定得停在座標為(R,C)的餡餅旁邊。 現在,你拿到了一張標記著餡餅矩陣中,每一塊餡餅含金幣數量的表格。那麼,按照規則,你最多可以拿到多少金幣呢? 比方說,奶牛們把餡餅排成如下的矩陣,矩陣中的數字表示該位置的餡餅中含金幣的數量:
6 5 3 7 9 2 7
2 4 3 5 6 8 6
4 9 9 9 1 5 8
以下是條合法的路線
按上述的路線進行走動,一共可以獲得6+4+9+9+6+5+8=47個金幣.按照規則,在這個矩陣中最多可以得到50個金幣,路線如下圖所示:
Input
第1行: 兩個用空格隔開的整數,R和C
第2..R+1行: 每行包含C個用空格隔開的正整數,依次表示一行中從左往右各個餡餅裡金幣的數量
Output
輸出一個正整數,表示你所能收集到的最大金幣數目
Sample Input
3 7
6 5 3 7 9 2 7
2 4 3 5 6 8 6
4 9 9 9 1 5 8
Sample Output
50

思路

看起來是道移動類DP水題,實際上有個坑
先來看思路:
題目告訴我們了可以怎麼走

下一步你可以走到座標為(r-1,c+1),(r,c+1),或者(r+1,c+1)的餡餅旁邊

所以直接得到動態轉移方程

dp[j][i]=max(dp[j-1][i-1],max(dp[j+1][i-1],dp[j][i-1]))+a[j][i];

所以再逐列推導

for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			dp[j][i]=max(dp[j-1][i-1],max(dp[j+1][i-1],dp[j][i-1]))+a[j][i];
		}
	}

因為只能向右、右下、右上走
所以起點到終點連成的對角線以下部分是到達不了的(這是一個坑)
增加判斷

if(j<=i){		
}

程式碼

所以程式碼就出來了

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[1010][1010];
int dp[1010][1010];
bool in(int x,int y){
	return x>=1&&y<=x&&x<=n&&y<=m;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	dp[1][1]=a[1][1];
	//DFS(1,1);
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			if(j<=i){
				dp[j][i]=max(dp[j-1][i-1],max(dp[j+1][i-1],dp[j][i-1]))+a[j][i];
			}
			
		}
	}
	cout<<dp[n][m];
	return 0;
}