洛谷P1736 创意吃鱼法(DP)

news/2024/9/28 9:57:15 标签: DP

题意大概就是要从一个n * m的矩阵当中,求出一个主对角线或者副对角线都是1,其它地方都是0的最大正方形,并输出它的边长。
最暴力的方法应该就是枚举,对于任何一点(i , j),枚举它作为正方形的左上顶点/右上顶点时,能够构成的合法的最大正方形,怎么判断这个正方形是合法的呢?最简单的应当就是分别判断主副对角线是否都为1,再判断其余部分是否都为0。但是这么枚举的话,多半是会TLE的,所以我们要考虑优化。

我们可以用DP进行优化:
dp[i,j]表示以(i , j)为左上顶点/右上顶点时能构成的最大正方形的边长。
显然如果map[i,j]=0,dp[i,j]=0.
为了方便,我们考虑从下往上,从右到左扫描整个矩阵。
初始化:dp[n,1-m] = map[n,1-m]==1? 1:0;

状态转移有点难讲清,可以看看代码中的解释,然后自己写一下样例理解理解。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<stack>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=2505;
int dp[maxn][maxn],map[maxn][maxn];

inline void read(int &ret)
{
	char c;
	while((c=getchar())&&(c>'9'||c<'0'));
	ret=0;
	while(c>='0'&&c<='9')	ret=ret*10+c-'0', c=getchar();
}

int main()
{
	int n,m;
	read(n);	read(m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			read(map[i][j]);
	int ans=0;
	for(int i=m;i>=1;--i)	
	{
		dp[n][i]= map[n][i]==1? 1:0;
		ans=max(ans,dp[n][i]);
	}
	for(int i=1;i<=n;++i)	dp[i][0]=dp[i][m+1]=0;	//为了方便,将(1-n,0) (1-n,m+1)都初始为0
	for(int i=n-1;i>=1;--i)
	{
		for(int j=m;j>=1;--j)
		{
			if(!map[i][j])	dp[i][j]=0;
			else
			{
				int mi1=0,mi2=0;	//mi1记录(i,j)作为左上顶点时,能够扩张的最大边长,同理mi2记录右上顶点
				if(dp[i+1][j+1])	//如果dp[i+1,j+1]!=0
				{
					int temp=dp[i+1][j+1];	//temp用于更新能够扩张的最大边长
					for(int k=j+1;k<=m&&k<=j+temp;++k)	//从左往右扫描,如果扫到1就更新temp并终止
						if(map[i][k])
						{
							temp=k-1-j;
							break;
						}
					for(int k=i+1;k<=n&&k<=i+temp;++k)	//同理,从上往下扫描
						if(map[k][j])
						{
							temp=k-1-i;
							break;
						}
					mi1=temp;	//将temp的值赋给mi1
				}
				if(dp[i+1][j-1])	//同理
				{
					int temp=dp[i+1][j-1];
					for(int k=j-1;k>=1&&k>=j-temp;--k)
						if(map[i][k])
						{
							temp=j-k-1;
							break;
						}
					for(int k=i+1;k<=n&&k<=i+temp;++k)
						if(map[k][j])
						{
							temp=k-i-1;
							break;
						}
					mi2=temp;
				}
				dp[i][j]=max(mi1,mi2)+1;
				ans=max(ans,dp[i][j]);
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}

PS:其实这个DP是自己打个表,手推出来的。时间复杂度看似n3,但其实只有n2+,所以还是阔以的。


http://www.niftyadmin.cn/n/942943.html

相关文章

bzoj 2957: 楼房重建

Description 小A的楼房外有一大片施工工地&#xff0c;工地上有N栋待建的楼房。每天&#xff0c;这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆&#xff0c;数自己能够看到多少栋房子。   为了简化问题&#xff0c;我们考虑这些事件发生在一个二维平面上。小…

洛谷P1855 榨取kkksc03(二维费用背包)

二维费用背包裸题。 #include<cstdio> #include<algorithm> #include<cstring> #include<algorithm> #include<iostream> #include<stack> #include<vector> #include<cmath> using namespace std; typedef long long ll; in…

bzoj 3165: [Heoi2013]Segment

Description 要求在平面直角坐标系下维护两个操作&#xff1a; 1.在平面上加入一条线段。记第i条被插入的线段的标号为i。 2.给定一个数k,询问与直线 x k相交的线段中&#xff0c;交点最靠上的线段的编号。 Input 第一行一个整数n&#xff0c;表示共n 个操作。 接下来…

洛谷P1880 [NOI1995]石子合并(区间DP)

石子合并是个经典的区间DP模型&#xff0c;然而之前并没有做过。。。 关于石子合并可以看下这个博客&#xff1a; https://blog.csdn.net/riba2534/article/details/76045531 代码如下&#xff1a; #include<cstdio> #include<iostream> #include<algorithm>…

bzoj 3672: [Noi2014]购票

Description 今年夏天&#xff0c;NOI在SZ市迎来了她30周岁的生日。来自全国 n 个城市的OIer们都会从各地出发&#xff0c;到SZ市参加这次盛会。全国的城市构成了一棵以SZ市为根的有根树&#xff0c;每个城市与它的父亲用道路连接。为了方便起见&#xff0c;我们将全国的 n 个城…

bzoj 3670: [Noi2014]动物园

Description 近日&#xff0c;园长发现动物园中好吃懒做的动物越来越多了。例如企鹅&#xff0c;只会卖萌向游客要吃的。为了整治动物园的不良风气&#xff0c;让动物们凭自己的真才实学向游客要吃的&#xff0c;园长决定开设算法班&#xff0c;让动物们学习算法。 某天&#x…

洛谷P1351 联合权值(无根树转化为有根树)

先将所给的图&#xff08;无根树&#xff09;转化为有根树&#xff0c;可以用一遍DFS完成。 转化为有根树后&#xff0c;对于i点&#xff0c;能够产生与它联合权值的点&#xff0c;要么是它第一个祖先&#xff08;或孙子&#xff09;&#xff0c;要么是与它深度相同的兄弟。i与…

bzoj 1529: [POI2005]ska Piggy banks

Description Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐.Input 第一行一个整数…