二維dp:創意吃魚
阿新 • • 發佈:2018-12-17
這是個對於我這類的菜鳥來說較為困難的題目 題目的大概意思哈~: 可愛貓貓家裡長方形大池子中有很多魚,她發現,把大池子視為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; }