cunzai_zsy0531

关注我

CF1198D Rectangle Painting 1 题解

Post time: 2021-02-01 18:33:46


传送门

这题是一道典型的区间dp。我们考虑一个矩形 \((x_1,y_1,x_2,y_2)\) (左上和右下角),它的最小答案显然可以通过从中间切开,左边和右边合并来计算。于是我们得到了一个动态规划基础模型:由小问题得出大问题答案。

\(f[x_1][y_1][x_2][y_2]\) 为矩形 \((x_1,y_1,x_2,y_2)\) 的最优解。首先初始化 \(f[x_1][y_1][x_2][y_2]=\max(x_2-x_1+1,y_2-y_1+1)\)

对行列分别考虑有:

对行:\(f[x_1][y_1][x_2][y_2]=\max\limits_{k=x_1}^{x_2-1}\{f[x_1][y_1][k][y_2]+f[k+1][y_1][x_2][y_2] \}\)

对列:\(f[x_1][y_1][x_2][y_2]=\max\limits_{k=y_1}^{y_2-1}\{f[x_1][y_1][x_2][k]+f[x_1][k+1][x_2][y_2] \}\)

最后 \(f[1][1][n][n]\) 的结果就是整个题的解。

做此类题目要敏锐观察题目中的条件并设计合适合理的状态,看看能否从小状态推至大状态,先通过这个写出暴力dp方程,然后再尝试求解。

点击查看代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=50+13;
int n,f[N][N][N][N];//f数组就是上面讲的记录答案的数组
char s[N][N];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%s",s[i]+1);
		for(int j=1;j<=n;++j) f[i][j][i][j]=(s[i][j]=='#');//对每个点的初始化
	}
	for(int x1=1;x1<=n;++x1)
	for(int y1=1;y1<=n;++y1)
	for(int x2=x1;x2<=n;++x2)//注意x2和y2的下界!
	for(int y2=y1;y2<=n;++y2)
		if(x1!=x2||y1!=y2) f[x1][y1][x2][y2]=max(x2-x1+1,y2-y1+1);
	for(int x1=n;x1>=1;--x1)
	for(int x2=x1;x2<=n;++x2)
	for(int y1=n;y1>=1;--y1)
	for(int y2=y1;y2<=n;++y2){
		for(int k=x1;k<x2;++k) f[x1][y1][x2][y2]=min(f[x1][y1][x2][y2],f[x1][y1][k][y2]+f[k+1][y1][x2][y2]);
		for(int k=y1;k<y2;++k) f[x1][y1][x2][y2]=min(f[x1][y1][x2][y2],f[x1][y1][x2][k]+f[x1][k+1][x2][y2]);
	}
	printf("%d\n",f[1][1][n][n]);
	return 0;
}
posted @ 2022-04-21 19:58  cunzai_zsy0531  阅读(38)  评论(0编辑  收藏  举报