[洛谷]P1736 創意喫魚法 (#記憶化搜索)

題目背景

感謝@throusea 貢獻的兩組數據

題目描述

回到家中的貓貓把三桶魚全部轉移到了她那長方形大池子中,然後開始思考:到底要以何種方法喫魚呢(貓貓就是這麼可愛,喫魚也要想好喫法 ^_*)。她發現,把大池子視爲01矩陣(0表示對應位置無魚,1表示對應位置有魚)有助於決定喫魚策略。

在代表池子的01矩陣中,有很多的正方形子矩陣,如果某個正方形子矩陣的某條對角線上都有魚,且此正方形子矩陣的其他地方無魚,貓貓就可以從這個正方形子矩陣“對角線的一端”下口,只一吸,就能把對角線上的那一隊鮮魚吸入口中。

貓貓是個貪婪的傢伙,所以她想一口喫掉儘量多的魚。請你幫貓貓計算一下,她一口下去,最多可以喫掉多少條魚?

輸入格式

有多組輸入數據,每組數據:

第一行有兩個整數n和m(n,m≥1),描述池塘規模。接下來的n行,每行有m個數字(非“0”即“1”)。每兩個數字之間用空格隔開。

對於30%的數據,有n,m≤100

對於60%的數據,有n,m≤1000

對於100%的數據,有n,m≤2500

輸出格式

只有一個整數——貓貓一口下去可以喫掉的魚的數量,佔一行,行末有回車。

輸入輸出樣例

輸入 #1複製

4 6
0 1 0 1 0 0
0 0 1 0 1 0
1 1 0 0 0 1
0 1 1 0 1 0

輸出 #1複製

3

說明/提示

右上角的

1 0 0
0 1 0
0 0 1

思路

拿到這題立馬想到了最大正方形那題,可是卻不會推方程。寫了個記憶化搜索。

怎麼去搜?對於任意一個子正方形,要滿足魚都在對角線,也就是對角線的數字不爲0;子正方形其他地方沒有魚,也就是對角線以外都是0。假如(x,y)有魚,那就可以dfs(x+1,y+1)和dfs(x+1,y-1)來判斷對角線上是否有魚。既然一個子正方形只有對角線有魚,那麼該正方形的數字和就是對角線的長度。所以利用二維前綴和把整個矩形總和算出來。

#include <stdio.h>
#include <iostream>
#define N 2501
using namespace std;
int a[N][N],dp[N][N][2],sum[N][N],n,m,s,orgx,orgy;
int tox[5]={0,1,0,-1,0},toy[5]={0,0,1,0,-1};
int dfs1(int x,int y)
{
	if(x>n || y>m ) return 0;
    //if(dp[x][y][0]) return dp[x][y][0];//爲什麼註釋?下面有解釋 
	register int cnt(sum[x][y]-sum[orgx-1][y]-sum[x][orgy-1]+sum[orgx-1][orgy-1]);
	if(cnt!=x-orgx+1 || a[x][y]==0)
	{
		return 0;
	}
	dp[x][y][0]=dfs1(x+1,y+1)+1;
	if(dp[x][y][0]) return dp[x][y][0];//爲什麼到這裏才進行記憶化?下面有解釋 
}
int dfs2(int x,int y)
{
	if(x>n || y<1 ) return 0;
	register int cnt(sum[x][orgy]-sum[orgx-1][orgy]-sum[x][y-1]+sum[orgx-1][y-1]);
	if(cnt!=x-orgx+1 || a[x][y]==0)
	{
		return 0;
	}
	dp[x][y][1]=dfs2(x+1,y-1)+1;
	if(dp[x][y][1]) return dp[x][y][1];
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	register int i,j,k;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			cin>>a[i][j];
			sum[i][j]=sum[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+a[i][j];
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			orgx=i;
			orgy=j;
			dp[i][j][0]=dfs1(i,j);
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			orgx=i;
			orgy=j;
			dp[i][j][1]=dfs2(i,j);
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			s=max(s,max(dp[i][j][0],dp[i][j][1]));
		}
	}
	cout<<s<<endl;
	return 0;
}

接下來來解答代碼註釋中的問題。

假設有如下4*4的矩形:

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

答案是3,但如果把註釋那句話放到前面,答案會就變成2。因爲dp[2][2][0]會先複製成2,再次枚舉到該點時會直接返回,而得不到右下角的正確矩形。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章