dp动态规划 方格取数

题目

方格取数

分析

        这道题要我们求的是走了两次的路径之和的最大值,我们首先可能会想到用两次dp来做,第一次选择最大的,然后把最大路径上的数字都置为空,第二次再选择最大的,然后相加就是题目最优解。但是我们这里必须得纠正一点,两次局部最优解之和不一定是整体最优解。

        我们在第一次选择最大的时候,可能会影响第二次的选择,导致了整体的最优受到影响。为了让整体的值更大,所以我们得小心地选,尽量避免影响第二条路径受到影响,使用这里我们需要考虑两条路一起选择。

        所以我们现在就来定义一下状态方程,f[i1][j1][i2][j2],表示第一条路径走到i1,j1, 第二条路径走到i2,j2位置的取数最大值,那么结果就是f[n][n][n][n], 其中i,j分别是行号和列号。

        但是用四维来表示是状态是不是有点大,于是我们考虑一下优化,因为每次只能向右或者向下走一步,所以每次i+1j不变或者i不变,j+1,每次是不是ij是不是总和只会+1,于是我们可以对其进行压缩变成i+j=k,所以可得上一个状态就是k-1,然后定义i1,i2为两条路径的行走状态,这样j=k-i,就压缩成了三维。

        于是我们来看如下状态

                第一条路径往下走,第二条路径往下走, 即f[k-1][i1-1][i2-1]
                第一条路径往下走,第二条路径往右走,即f[k-1][i1-1][i2]
                第一条路径往右走,第二条路径往右走,即f[k-1][i1][i2]
                第一条路径往右走,第二条路径往下走,即f[k-1][i1][i2-1]

        然后我们再看一下其他状态,我们得定义一个w[i][j]来表示坐标上的值,然后对于两条路径同时走,就可能会出现重合的这种状态,但是对于如上问题,每个点上的值只能取一次。我们取上一个状态所取得数为t,于是当重合时,即i1=i2,j1=j2时,那么t=w[i1][j1], 不重合的话,t=w[i1][j1]+w[i2][j2]

代码

#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;
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值