1. 程式人生 > >二維dp:創意吃魚

二維dp:創意吃魚

這是個對於我這類的菜鳥來說較為困難的題目 題目的大概意思哈~: 可愛貓貓家裡長方形大池子中有很多魚,她發現,把大池子視為01矩陣(0表示對應位置無魚,1表示對應位置有魚)有助於決定吃魚策略。 在代表池子的01矩陣中,有很多的正方形子矩陣,如果某個正方形子矩陣的某條對角線上都有魚,且此正方形子矩陣的其他地方無魚,貓貓就可以從這個正方形子矩陣“對角線的一端”下口 ,只需一吸。問貓貓一口吃掉的魚最多是多少。

理一理: 就是讓我們在一個01矩陣中找一個以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

可以發現最多是一個邊長為3 的矩陣,對角線最長為3 動規的設定規則: 求什麼,設什麼 因此我們要列舉正方形,一般來說就是以f[i][j]為正方形的一角 現在的問題就是怎麼判斷他是一個符合條件的正方形 因為除了對角線是1以外,正方形的其餘部分都為0,那麼這個正方形的邊長取決於邊長上連續的0的個數。

我們用**Left[]**記錄當前位置左邊連續0的個數, **Up[]**記錄當前位置以上連續0的個數, **Right[]**記錄當前位置右邊連續0的個數(都包括自己)

分成兩個部分,1.以f[i][j]為右下角,即推出右對角線;2.以f[i][j]為左下角來一遍,推出左對角線 。

#include<bits/stdc++.h>
using namespace std;
int a[3010][3010]={};
int f[3010][3010],g[3010][3010];
int Left[3010][3010],Right[3010][3010],Up[3010][3010];
int ans;
int main()
{
	freopen("meal.in","r",stdin);
	freopen("meal.out","w",stdout); 
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	  cin>>a[i][j];
	//以f[i][j]為右下角,即推出右對角線
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	  {
	    if(a[i][j]==0)
	    {
	    	Left[i][j]=Left[i][j-1]+1;//左側有多少個連續的0 
	    	Up[i][j]=Up[i-1][j]+1;//上方有多少個連續的0 
		}
		else
		 f[i][j]=1;//邊長為1的正方形對角線個數為1 
	  }
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	  {
	  	if(a[i][j]==0)continue;
	  	 f[i][j]=min(min(f[i-1][j-1],Left[i][j-1]),Up[i-1][j])+1;
	  	 //(由“農田個數”發展而來) 
	  	 //對角線的長度取決於這個正方形的邊長,而此時f[i][j]代表的是矩陣的右下角
		 //因為正方形除了對角線其餘部分都為0,
		 //因此與 當前位置左邊連續的0的個數 和 上方連續0的個數 的最小值+1(因為要算上當前位置)可以推出正方形邊長  
	     ans=max(ans,f[i][j]);//更新最大值 
	  }
	//以f[i][j]為左下角來一遍,意思同上,推出左對角線 
	memset(f,0,sizeof(f));
	for(int i=1;i<=n;i++)
	 for(int j=m;j>=1;j--)
	  {
	    if(a[i][j]==0)
	    	Right[i][j]=Right[i][j+1]+1;//當前位置右邊連續0的個數 
		else
		 f[i][j]=1; 
	  }
	for(int i=1;i<=n;i++)
	 for(int j=m;j>=1;j--)
	  {
	  	if(a[i][j]==0)continue;
	  	 f[i][j]=min(min(f[i-1][j+1],Right[i][j+1]),Up[i-1][j])+1; 
	     ans=max(ans,f[i][j]);
	 }
	cout<<ans<<endl;
	return 0;
}