1. 程式人生 > 實用技巧 >洛谷P1736 創意吃魚法

洛谷P1736 創意吃魚法

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; 
}