题目
分析
这道题要我们求的是走了两次的路径之和的最大值,我们首先可能会想到用两次dp来做,第一次选择最大的,然后把最大路径上的数字都置为空,第二次再选择最大的,然后相加就是题目最优解。但是我们这里必须得纠正一点,两次局部最优解之和不一定是整体最优解。
我们在第一次选择最大的时候,可能会影响第二次的选择,导致了整体的最优受到影响。为了让整体的值更大,所以我们得小心地选,尽量避免影响第二条路径受到影响,使用这里我们需要考虑两条路一起选择。
所以我们现在就来定义一下状态方程,,表示第一条路径走到
, 第二条路径走到
位置的取数最大值,那么结果就是
, 其中i,j分别是行号和列号。
但是用四维来表示是状态是不是有点大,于是我们考虑一下优化,因为每次只能向右或者向下走一步,所以每次,
不变或者
不变,
,每次是不是
和
是不是总和只会+1,于是我们可以对其进行压缩变成
,所以可得上一个状态就是
,然后定义
为两条路径的行走状态,这样
就压缩成了三维。
于是我们来看如下状态
第一条路径往下走,第二条路径往下走, 即
第一条路径往下走,第二条路径往右走,即
第一条路径往右走,第二条路径往右走,即
第一条路径往右走,第二条路径往下走,即
然后我们再看一下其他状态,我们得定义一个来表示坐标上的值,然后对于两条路径同时走,就可能会出现重合的这种状态,但是对于如上问题,每个点上的值只能取一次。我们取上一个状态所取得数为
,于是当重合时,即
时,那么
不重合的话,
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=10;
int f[N+N][N][N],w[N][N];
int n,x,y,c;
int main()
{
cin>>n;
while(cin>>x>>y>>c,x||y||c)
w[x][y]=c;
for(int k=2;k<=n+n;k++)//k是两条路径的总长度
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)
continue;//记得判断边界情况,越界了就跳过
//两条路径取出的数的总和
int t=w[i1][j1];
if(i1!=i2)//如果两条路径不重叠的话
t+=w[i2][j2];
int &v=f[k][i1][i2];//引用,下文的v都是f[k][i1][i2]的简写,加上引用就可以修改
//模拟走的四个过程并求出最大值
v=max(v,f[k-1][i1-1][i2-1]+t);//都向下
v=max(v,f[k-1][i1-1][i2]+t);//下右
v=max(v,f[k-1][i1][i2-1]+t);
v=max(v,f[k-1][i1][i2]+t);
}
cout<<f[n+n][n][n];
return 0;
}