[棋盘型DP] 棋盘型DP选练

棋盘型DP
考虑记录位置

常见题型高维,多路径

T1 传纸条

传送门

好题!
CCF出品,必属精品!

考虑转化题意
从起点到终点只能往右或往下
求路径上值之和最大

考虑记录状态
棋盘型DP
考虑记录坐标f[x1][y1][x2][y2]

考虑转移
f[x1][y1][x2][y2]=max(f[x1][y1-1][x2][y2-1],f[x1][y1-1][x2-1][y2],f[x1-1][y1][x2][y2-1],f[x1-1][y1][x2-1][y2])+a[x1][y1]+a[x2][y2]
即四种走下来的方法取max
考虑保证路径不重合
发现步数一致(当然是一致的
考虑曼哈顿距离
显然每个状态都在一个棋盘的上三角形(还是等腰的)的斜边边界上
显然要选两个点
而且下面一个点确定了上面的点就只能在斜边边界上往上找了

考虑优化
上面已经提到两个点的相对位置
考虑枚举下面的点,利用这个相对位置算出上面的点

#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
	int i=0,f=1;char ch=0;
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') ch=getchar(),f=-1;
	while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int N=100;
int a[N][N],n,m,f[N][N][N];

int main(){
	n=in,m=in;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			a[i][j]=in;
	for(int step=3;step<=n+m;++step)
		for(int x1=1,y1=step-x1;x1<=n;++x1,--y1){
			if(y1>m) continue;
			for(int x2=x1+1,y2=step-x2;x2<=n;++x2,--y2){
				if(y2>m) continue;
				f[step][x1][x2]=max(max(f[step-1][x1][x2],f[step-1][x1-1][x2-1]),max(f[step-1][x1-1][x2],f[step-1][x1][x2-1]));
				f[step][x1][x2]+=a[x1][y1]+a[x2][y2];
			}
		}
	printf("%d\n",f[n+m-1][n-1][n]);
	// for(int k=1;k<=m+n;++k)
	// 	for(int i=1;i<=n;++i)
	// 		for(int j=1;j<=n;++j)
	// 			printf("%d(%d,%d,%d) ",f[k][i][j],k,i,j);
	return 0;
}

hurray!基本上是自己做出来的

T2 方格取数

传送门

棋盘型DP
和传纸条一样,发现每一步都有一个层次:
上三角形的斜边,只有斜边
没有前效性,DP妥妥的

#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
	int i=0,f=1;char ch=0;
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') ch=getchar(),f=-1;
	while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int N=50;
int a[N][N],n,m,f[N][N][N];

int main(){
	n=in;
	int x,y,w;
	while(x=in){
		y=in,w=in;
		a[x][y]=w;
	}
	for(int step=2;step<=n+n;++step){
		for(int x1=1,y1=step-x1;x1<=n&&y1>=1;++x1,--y1){
			for (int x2=1, y2=step-x2;x2<=n&&y2>=1;++x2, --y2) {
				f[step][x1][x2]=max(max(f[step-1][x1][x2], f[step-1][x1-1][x2-1]), max(f[step-1][x1-1][x2], f[step-1][x1][x2-1]));
				f[step][x1][x2]+=(x1==x2&&y1==y2)?a[x1][y1]:(a[x1][y1]+a[x2][y2]);
			}
		}
	}
	printf("%d\n",f[n+n][n][n]);
	// for(int k=1;k<=m+n;++k)
	// 	for(int i=1;i<=n;++i)
	// 		for(int j=1;j<=n;++j)
	// 			printf("%d(%d,%d,%d) ",f[k][i][j],k,i,j);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值