SSL 1633 农田个数 (数数版恶心DP)

这篇博客介绍了如何使用动态规划方法解决一个关于计算无水正方形农田数量的问题。作者给出了两种不同的解题思路,分别通过计算每个位置的最大正方形边长和统计不同边长农田的数量来得出答案。代码示例展示了如何实现这两个方法,适用于理解动态规划在解决几何问题上的应用。

题目描述:

你的老家在河北农村。过年时,你回老家去拜年。你家有一片N∗MN*MNM农田,将其看成一个N∗MN*MNM的方格矩阵,有些方格是一片水域。你的农村伯伯听说你是学计算机的,给你出了一道题: 他问你:这片农田总共包含了多少个不存在水域的正方形农田
两个正方形农田不同必须至少包含下面的两个条件中的一条
边长不相等
左上角的方格不是同一方格

输入格式:
输入数据第一行为两个由空格分开的正整数N、MN、MNM(1<=mmm 第2行到第N+1N+1N+1行每行有MMM个数字(000111),描述了这一片农田。000表示这个方格为水域,否则为农田(注意:数字之间没有空格,而且每行不会出现空格)

输出格式
不存在水域的不同正方形农田个数。

输入样例:
3 3
110
110
000

输出样例:
5


解题思路:

方法一:

我们可以通过正方形农田的边长以及他的右下角坐标来描述这块农田,考虑DP。

fi,jf_{i,j}fi,j 表示以 (i,j)(i,j)(i,j) 为右下角的最大正方形农田边长。显然,若 (i,j)(i,j)(i,j) 本身为水域,那么很显然无法构成任何以 (i,j)(i,j)(i,j) 作为右下角的无水正方形农田。因此易得若 (i,j)(i,j)(i,j) 为水域,则 fi,j=0f_{i,j}=0fi,j=0
(i,j)(i,j)(i,j) 为农田,那么经过一点实践就能推出,以 (i,j)(i,j)(i,j) 为右下角的正方形农田的最大边长为:
fi,j=min( fi−1,j,fi,j−1,fi−1,j−1 )+1f_{i,j}=min(~f_{i-1,j},f_{i,j-1},f_{i-1,j-1}~)+1fi,j=min( fi1,j,fi,j1,fi1,j1 )+1

不难发现,若要用 (i,j)(i,j)(i,j) 为右下角构成一个边长大于 111 的无水正方农田,显然必须要使 (i−1,j),(i,j−1),(i−1,j−1)(i-1,j),(i,j-1),(i-1,j-1)(i1,j),(i,j1),(i1,j1) 都为农田,否则就构不成一个正方形。
方程最后的 +1+1+1 即表明 (i,j)(i,j)(i,j) 本身也是一个边长为 111 的无水正方形农田,与 min( fi−1,j,fi,j−1,fi−1,j−1 )min(~f_{i-1,j},f_{i,j-1},f_{i-1,j-1}~)min( fi1,j,fi,j1,fi1,j1 ) 加上,即为最长边正方形。

因此 fff 的动态转移方程即为:
fi,j={0mapi,j=0min( fi−1,j,fi,j−1,fi−1,j−1 )+1mapi,j=1 f_{i,j}=\begin{cases} 0&map_{i,j}=0\\ min(~f_{i-1,j},f_{i,j-1},f_{i-1,j-1}~)+1&map_{i,j}=1 \end{cases} fi,j={0min( fi1,j,fi,j1,fi1,j1 )+1mapi,j=0mapi,j=1

我们能够发现,若 fi,jf_{i,j}fi,j 为以 (i,j)(i,j)(i,j) 为右下角的最大正方形边长,那么显然(i,j)(i,j)(i,j) 为右下角的所有正方形即为 fi,jf_{i,j}fi,j,且他们在整张地图中都是题目中所描述的“互不相同”的
因此答案还可以这么来:
ans=∑i=1n∑j=1mfi,jans=\sum_{i=1}^{n} \sum_{j=1}^{m} f_{i,j}ans=i=1nj=1mfi,j

#include <iostream>
using namespace std;
int n,m;
char map[1001][1001];
int f[1001][1001]={0},f1[1000010]={0};
long long sum[1000010]={0},ans=0;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	  {
	  	for(int j=1;j<=m;j++)
	  	  {
	  	  	cin>>map[i][j];
	  	  	map[i][j]-='0';
	  	  	if(map[i][j]) f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;
	  	  	else f[i][j]=0;
	  	  	ans+=f[i][j];
		  }
	  }
	cout<<ans;
	return 0;
} 

方法二:

f1kf1_{k}f1k,表示 所有的fi,jf_{i,j}fi,j 值为 kkk,(即最大边长为 kkk 的农田) 的个数。

此时还需要一个 sumisum_isumi 表示在整张地图中,边长为 iii 的所有农田的个数,那么此时易得每一个边长为 i+1i+1i+1 的正方形中都必定含有至少一个边长为 iii 的正方形,并且有某些边长为 iii 的正方形独立(注意,这里的独立既指周围都是水域的正方形,亦指在若右下角为 (i,j)(i,j)(i,j),那么边长等于 fi,jf_{i,j}fi,j 的那些正方形,这种正方形往往还能存在于大正方形的左上角)存在,他们的个数即 f1if1_{i}f1i
因此可推知
sumi=sumi+1+f1isum_{i}=sum_{i+1}+f1_{i}sumi=sumi+1+f1i

此时,只需要每局地图中可能的所有正方形的边长,然后将 sumisum_isumi 累加,即为满足条件的正方形个数。

ans=∑i=1nsumians=\sum_{i=1}^{n} sum_ians=i=1nsumi

#include <iostream>
using namespace std;
int n,m;
char map[1001][1001];
int f[1001][1001]={0},f1[1000010]={0};
long long sum[1000010]={0},ans=0;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	  {
	  	for(int j=1;j<=m;j++)
	  	  {
	  	  	cin>>map[i][j];
	  	  	map[i][j]-='0';
	  	  	if(map[i][j]) f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;
	  	  	else f[i][j]=0;
	  	  	f1[f[i][j]]++;
		  }
	  }
	for(int i=n;i>=1;i--)
	  sum[i]=sum[i+1]+f1[i],ans+=sum[i];
	cout<<ans;
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值