[TK] 盖房子 hzoi-tg#262

同机房大佬也写了这题的 题解.

通解分析

此类问题我通常喜欢归纳成一类,即阻碍联通的坐标DP.
既然是阻碍联通,那么此类问题的通用思路是这样的:
首先将dp数组图形化. 一般用一个特定图形上的特定点来标识整个图形. 比如假如我需要找正方形的最大面积,我可以用左下角坐标来表示整个正方形. 找三角形最大面积,我可以用最上方顶点来表示整个三角形. 但这个标识一定要是确定的,假如我只用 顶点 来表示三角形,那么可表示的就太多了.
既然是坐标,那么我们一般使用一个二维dp数组. 数组里存放的是这个图形的相关信息,使得我们能完全确定这个图形,比如边长等.
随后,我们根据图形的特殊性,构造出一种用周围坐标推出当前坐标的状态转移方程.
我们来看一下具体的实例.

问题处理

对于这个题。依据上面的思路,我们定义一个二维数组 f[i][j] ,其中 (i,j) 为正方形左下角的坐标, f[i][j] 中存储正方形的最大边长.
接下来我们这样初始化:令未损坏的坐标的最大边长都为 1 ,其正确性不必证明.
然后进行状态转移方程的推理.
我们先来看从 1 如何变成 2,这很容易想,因为我们定义的是正方形左下角的坐标,想要让这个坐标上的正方形边长为 2 ,只需要在 1 的基础上,判断其右方,上方,右上方的三个格子是不是 1 即可.
接下来看如何从 2 变成 3.

1 0 1 1
1 1 1 0
1 1 1 1
1 1 1 1

这是一块 4×4 大小的地块. 对它进行一次 1 变成 2 的操作,使它变成这样:

1 0 1 1
1 1 1 0
2 2 1 1
2 2 2 1

注意到最左下角可以变为3,而它的右方,上方,右上方的三个格子都为2.

如此尝试几次,我们得出结论:一个格子右方,上方,右上方的三个格子都不小于 n 时,该格子为 n+1.

所以有状态转移方程:

f[i][j]=min{f[i1][j]f[i][j+1]f[i1][j+1]+1

因为每一次更新都要在上一数字的基础上,所以状态转移方程需要遍历 k 次(其中 k 是最大正方形边长,一般来说是 min(m,n)

因此,该程序的时间复杂度为 O(nm·min(m,n)),近似于 n3 .

但是这题 n=1000 的时候必炸,所以我们要想办法优化一下.

注意到,假如我们从右上角开始遍历,那么更新左下角正方形需要的所有正方形都会在它之前被更新,因此我们这样遍历只需要更新一次. 时间复杂度为 O(mn).

代码实现

点击查看代码
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			cin>>f[i][j];
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=m;j>=1;--j){
			if(f[i][j]){
				f[i][j]=min({f[i-1][j],f[i][j+1],f[i-1][j+1]})+1;
				ans=max(ans,f[i][j]);
			}
		}
	}
	cout<<ans;
posted @   HaneDaniko  阅读(38)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示