洛谷P1736 創意吃魚法
阿新 • • 發佈:2020-08-24
P1736 創意吃魚法
這道題目是一道dp
問題
我們直接考慮dp
陣列的設定
用dp[i][j]
表示在i
j
這個點的最優吃魚方案
發現在dp[i][j]
這個點的最優答案只與這個點上面能連起來的0
的個數,左邊或右邊能連起來的0
的個數和dp[i-1][j-1]
或dp[i-1][j+1]
有關
發現我們這樣就只用維護三個陣列
1.記錄當前點左邊或右邊連續的0的個數
2.記錄當前點上邊連續的0的個數
3.記錄當前點的最優方案
但是我們發現對角線有從左上到右下和從右上到左下之分
所以就會有左邊和右邊的0
的個數之分
當然,dp
方程也會不太一樣
當我們取的是左上到右下時,s1
記錄左邊連續0
的個數
方程為:dp[i][j]=min(dp[i-1][j-1],min(s1[i][j-1],s2[i-1][j]))+1;
當我們取的是右上到左下時,s1
記錄右邊連續0
的個數
方程為:dp[i][j]=min(dp[i-1][j+1],min(s1[i][j+1],s2[i-1][j]))+1;
至於為啥取最小值,我們可以畫一個圖
根據圖的意思來表示我們的方程為啥取min
也不是特別難理解
當然不是在列舉每一個的時候都需要去用這個dp
方程
只有在a[i][j]
為1
時我們才會用到【a[i][j]
存的是這個矩陣】
而當a[i][j]
為0
時我們得去維護或者更新s1
陣列和s2
陣列
在兩次求dp
的過程中【一次是左上到右下,另一次是右上到左下】
我們在中間需要重新memset
這個s1
陣列和dp
陣列
從而不讓兩次dp
產生干擾
最後取每個dp[i][j]
max
,因為dp
陣列會在過程中被memset
下放程式碼
#include<bits/stdc++.h> using namespace std; int a[2501][2501],s1[2501][2501],s2[2501][2501],dp[2501][2501],ans; int main() { int n,m; cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&a[i][j]); if(!a[i][j]) { s1[i][j]=s1[i][j-1]+1; s2[i][j]=s2[i-1][j]+1; } else dp[i][j]=min(dp[i-1][j-1],min(s1[i][j-1],s2[i-1][j]))+1; ans=max(ans,dp[i][j]); } memset(s1,0,sizeof(s1)); memset(s2,0,sizeof(s2)); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) for(int j=m;j>=1;j--) { if(!a[i][j]) { s1[i][j]=s1[i][j+1]+1; s2[i][j]=s2[i-1][j]+1; } else dp[i][j]=min(dp[i-1][j+1],min(s1[i][j+1],s2[i-1][j]))+1; ans=max(ans,dp[i][j]); } cout<<ans; return 0; }