题目链接
题意:
有n*n的方格,有些格子上有数,有些格子上没有数(可以认为是0),现从(1,1)走到(n,n)走两次,走过之后,上面的数就会变为0,问两次走的路径取得的数字之和最大为多少?
思路:
状态表示:
d p [ i 1 ] [ i 2 ] [ k ] dp[i1][i2][k] dp[i1][i2][k]表示从(1,1)开始两条路径分别走到 ( i 1 , k − i 1 ) , ( i 2 , k − i 2 ) (i1,k-i1),(i2,k-i2) (i1,k−i1),(i2,k−i2)取得的数字的最大值。
其实 k = i 1 + j 1 = i 2 + j 2 k=i1+j1=i2+j2 k=i1+j1=i2+j2,我们让两个路径的横纵坐标加和相同,可以理解为步长(但不是步长),步长相同刚好能枚举在同一时刻的状态,因为他们走的步数一样嘛。
有步长了,就可以根据横坐标推出纵坐标了。所以就很巧妙
当前状态可由四种情况来转移:
上一个状态都是步长减一,表示的是两条路径步长都减去一,两条路径是同时进行的。
- d p [ i 1 − 1 ] [ i 1 − 1 ] [ k − 1 ] dp[i1-1][i1-1][k-1] dp[i1−1][i1−1][k−1]表示两条路径都是从当前点的上方转移而来的,当前点指的是两条路径分别对应的当前点,不一定一样
- d p [ i 1 − 1 ] [ i 2 ] [ k − 1 ] dp[i1-1][i2][k-1] dp[i1−1][i2][k−1]表示第一条路径从当前点(第一条路径的当前点)的上方转移而来的,第二条路径从当前点(第二条路径的当前点)的左边转移而来
- d p [ i 1 ] [ i 2 − 1 ] [ k − 1 ] dp[i1][i2-1][k-1] dp[i1][i2−1][k−1]表示第一条路径从当前点(第一条路径的当前点)的左边转移而来的,第二条路径从当前点(第二条路径的当前点)的上方转移而来
- d p [ i 1 ] [ i 2 ] [ k − 1 ] dp[i1][i2][k-1] dp[i1][i2][k−1]表示两条路径都是从当前点的左边转移而来的,当前点指的是两条路径分别对应的当前点,不一定一样
状态来自这四种情况
所以只要取他们的最大值就行了
注意:
都是从上方或者左边转移的,他们不一定来自同一个点,所以只有
i
1
=
i
2
i1=i2
i1=i2时,他们才来自同一个点,这时候,取的数就会只有一个,因为一条路径取过之后方格上的数就会变为0
#include<bits/stdc++.h>
using namespace std;
int a[15][15],dp[15][15][30];
int main()
{
int n;cin>>n;
int x,y,num;
//都为0时才结束
while(cin>>x>>y>>num,x||y||num)
a[x][y]=num;
for(int k=2;k<=n+n;k++)//k=i1+j1=i2+j2
{
for(int i1=1;i1<=n;i1++)
{
for(int i2=1;i2<=n;i2++)
{
int j1=k-i1,j2=k-i2;
if(j1>=1&&j1<=n&&j2>=1&&j2<=n)//纵坐标必须满足条件
{
int w = a[i1][j1];
if(i1!=i2) w+=a[i2][j2];//不是同一个点就加两个方格对应的数
int &q = dp[i1][i2][k];//太长了,用引用减少代码量
q=max(q,dp[i1-1][i2-1][k-1]+w);
q=max(q,dp[i1-1][i2][k-1]+w);
q=max(q,dp[i1][i2-1][k-1]+w);
q=max(q,dp[i1][i2][k-1]+w);
}
}
}
}
cout<<dp[n][n][n+n]<<'\n';
return 0;
}