【Codeforces Round #576 (Div. 2)】Rectangle Painting 1【记忆化搜索】

本文深入探讨了一道经典的DP(Dynamic Programming)问题——在给定的n*n网格中,求将整个区域全部涂成白色所需的最少花费。文章分享了从初见题目时的困惑到找到正确状态定义和转移思路的心路历程,最终通过记忆化搜索实现了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给出一个 n∗nn*nnn 的网格,其中有一些为黑,有一些为白。现在将一块 w∗hw*hwh 的区域全部涂成白色的花费是 max(w,h)max(w,h)max(w,h),问将整个区域全部涂成白色的最少花费是多少?(1≤n≤50)(1\leq n\leq 50)(1n50)


思路:

无论是从题干还是从数据范围,都可以比较明显地看出这是一道 DPDPDP。而 DPDPDP 问题最关键的就是状态怎么定,如果状态定对了,问题将迎刃而解。

比赛时的思路是 dp[i][j]dp[i][j]dp[i][j] 表示从 (1,1)(1,1)(1,1)(i,j)(i,j)(i,j) 的区域全部涂成白色的最少花费。但是不知道如何转移这个状态,属于那种脑袋晕晕没有思路的状态,然后就放弃了…现在想想,当时发现状态没有很好的转移思路的时候,应该要及时更换思路,不能一条路走死了。

赛后知道了正确的状态应该是 dp[x1][y1][x2][y2]dp[x_1][y_1][x_2][y_2]dp[x1][y1][x2][y2],表示将 (x1,y1)(x_1,y_1)(x1,y1)(x2,y2)(x_2,y_2)(x2,y2) 的区域全部涂成白色的最少花费。转移就直接横纵枚举网格分割点,记忆化搜索即可。


总结:

DPDPDP 的问题就是这样,想不出状态或想错状态的时候觉得这个问题真难,搞懂之后就开始嫌这题怎么简单,我当时怎么没做出来…(哭

反思一下的话,这题犯了两个比较明显的错误。第一个是一开始定完状态之后,发现不好转移然后没有再继续思考,而是直接放弃。缺乏状态定错进行转换的能力!

第二个是没有深挖题干。nnn 只有 505050,结果上来就只定一个二维状态,可真是浪费这么小的空间,犯了不仔细研究数据范围的大忌。这里必须再插一句,ACM\text{ACM}ACM 的世界里,题干和数据范围是非常重要的,没有数据范围空谈解决方法是极其不明智的,因此不充分利用好数据范围的信息,不根据数据范围来估算时间空间复杂度,属实属于 zzzzzz 行为。

50−O(n5),102−O(n4),500−O(n3),103−O(n2logn),104−O(n2),105/106−O(nlogn)50-O(n^5),10^2-O(n^4),500-O(n^3),10^3-O(n^2logn),10^4-O(n^2),10^5/10^6-O(nlogn)50O(n5),102O(n4),500O(n3),103O(n2logn),104O(n2),105/106O(nlogn)


代码:

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = a; i <= b; i++)
const int N = 50+10;
using namespace std;
 
int n,sum[N][N],dp[N][N][N][N];
char s[N][N];
 
int dfs(int x1,int y1,int x2,int y2){
	if(dp[x1][y1][x2][y2] != -1) return dp[x1][y1][x2][y2];
	int cnt = sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
	if(cnt == 0) return (dp[x1][y1][x2][y2] = 0);
	int ans = max(y2-y1+1,x2-x1+1);
	rep(i,y1,y2-1){
		int t1 = dfs(x1,y1,x2,i);
		int t2 = dfs(x1,i+1,x2,y2);
		ans = min(ans,t1+t2);
	}
	rep(i,x1,x2-1){
		int t1 = dfs(x1,y1,i,y2);
		int t2 = dfs(i+1,y1,x2,y2);
		ans = min(ans,t1+t2);	
	}
	return (dp[x1][y1][x2][y2] = ans);
}
 
int main()
{
	scanf("%d",&n);
	rep(i,1,n) scanf("%s",s[i]+1);
	rep(i,1,n)
		rep(j,1,n){
			sum[i][j] = sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
			if(s[i][j] == '#') sum[i][j]++; //黑色块个数
		}
	memset(dp,-1,sizeof dp);
	printf("%d\n",dfs(1,1,n,n));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gene_INNOCENT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值