設有 N×N 的方格圖,我們在其中的某些方格中填入正整數,而其它的方格中則放入數字0。如下圖所示:
某人從圖中的左上角 A 出發,可以向下行走,也可以向右行走,直到到達右下角的 B 點。
在走過的路上,他可以取走方格中的數(取走後的方格中將變爲數字0)。
此人從 A 點到 B 點共走了兩次,試找出兩條這樣的路徑,使得取得的數字和爲最大。
輸入格式
第一行爲一個整數N,表示 N×N 的方格圖。
接下來的每行有三個整數,第一個爲行號數,第二個爲列號數,第三個爲在該行、該列上所放的數。
一行“0 0 0”表示結束。
輸出格式
輸出一個整數,表示兩條路徑上取得的最大的和。
數據範圍
N≤10
輸入樣例:
8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0
輸出樣例:
67
做法1:dfs第一次走的所有路徑,將走過的點標記一下,終點進行正常二維dp。
做法2:四維dp,dp[i][j][k][r] 表示有兩個人同時走,第一個人走到(i, j) 第二個人走到(k, r)的時候能獲得的最大價值。
兩個人都要從上邊或者左邊的格子轉移過來,那麼有四種不同的轉移方式,轉移方程是:dp[i][j][k][r] = max(dp[i-1][j][k][r], dp[i-1][j][k][r-1], dp[i][j-1][k-1][r], dp[i][j-1][k][r-1])+a[i][j]+a[k][r]
如果兩個人走到同一個點,a[i][j] 和 a[k][r]只能加一次,那麼減掉一個就好。
做法1 : dfs+dp 做法 :
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 15;
int a[N][N],vis[N][N],dp[N][N],ans,n;
void dfs(int x,int y,int sum)
{
if(x>n||y>n) return;
vis[x][y]=1;
if(x==n&&y==n)
{
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
dp[i][j]+=max(dp[i-1][j],dp[i][j-1]);
if(!vis[i][j]) dp[i][j]+=a[i][j];
}
ans=max(ans,sum+dp[n][n]);
}
dfs(x+1,y,sum+a[x+1][y]);
vis[x+1][y]=0;
dfs(x,y+1,sum+a[x][y+1]);
vis[x][y+1]=0;
}
int main()
{
int x,y,z;
cin>>n;
while(cin>>x>>y>>z && (x||y||z))
a[x][y]=z;
dfs(1,1,a[1][1]);
cout<<ans<<endl;
return 0;
}
做法2: 四維dp做法
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 15;
int a[N][N],dp[N][N][N][N];
int main()
{
int n,x,y,z;
cin>>n;
while(cin>>x>>y>>z && (x||y||z))
a[x][y]=z;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
for(int r=1;r<=n;r++)
{
int &v=dp[i][j][k][r];
v=max(v,dp[i-1][j][k-1][r]+a[i][j]+a[k][r]);
v=max(v,dp[i-1][j][k][r-1]+a[i][j]+a[k][r]);
v=max(v,dp[i][j-1][k-1][r]+a[i][j]+a[k][r]);
v=max(v,dp[i][j-1][k][r-1]+a[i][j]+a[k][r]);
if(i==k&&j==r) v-=a[i][j];
}
cout<<dp[n][n][n][n]<<endl;
return 0;
}