1. 程式人生 > >【題目整理】基礎dp

【題目整理】基礎dp

目錄

 

HDU2859 Phalanx(最大對稱矩陣)

HDU1176 免費餡餅

HDU1024 Max Sum Plus Plus(滾動陣列)


HDU2859 Phalanx(最大對稱矩陣)

【題意】

給一個矩陣,求這個矩陣的最大對稱矩陣長度。

【解題思路】

設dp[i][j]為第(i,j)位置能夠構成最大對稱矩陣的長度。

當我們算到s[i][j]時,每次我們只需要將它上方的和右方的依次比較,看是否相同,注意這裡不能只比較s[i-1][j]和s[i][j+1],因為可能出現不符合的情況,如:

zaba

cbab

abbc

cacq

當我們比較到紅色的b的時候,如果只比較與他相鄰的兩個即綠色的b,就會從dp[i-1][j+1]多+1,而顯然藍色的部分不同。

當i!=0&&dp[i-1][j+1]>i-a時,dp[i][j]=dp[i-1][j+1]+1

當i!=0&&dp[i-1][j+1]<i-a (其中a為從當前位置找,找到的第一個不相等的x的位置,所以i-a就為最大的對稱矩陣的長度)時,dp[i][j]=i-a;

當i==0時,dp[i][j]=1;

題解轉自:https://blog.csdn.net/yangyafeiac/article/details/9445397

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
char s[maxn][maxn];
int dp[maxn][maxn];
int main()
{
    int n;
    while(~scanf("%d",&n)&& n)
    {
        for(int i=0;i<n;i++)
            scanf("%s",s[i]);
        int ans=0;
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(i==0)dp[i][j]=1;
                else
                {
                    int a=i;
                    int b=j;
                    while(s[a][j]==s[i][b])
                    {
                        a--;
                        b++;
                        if(a<0 || b>=n)break;
                    }
                    if(dp[i-1][j+1]<i-a)dp[i][j]=dp[i-1][j+1]+1;
                    else dp[i][j]=i-a;
                }
                printf("dp[%d][%d]=%d\n",i,j,dp[i][j]);
                ans=max(ans,dp[i][j]);
            }
        }
        printf("%d\n",ans);
    }
}

 

HDU1176 免費餡餅

【題意】

t秒x點掉餡餅。能不能接到看t-1秒gameboy是不是在x或x-1或x+1位置。最多可以得到多少餡餅。

【解題思路】

設dp[i][j]為第i秒在第j個位置能夠得到的最大餡餅數。所以易得狀態轉移方程:

dp[i][j]=max(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1])+num[i][j]。(num[i][j]為第i秒在第j給位置能夠得到的餡餅數)

然後就是注意初始化。也就是第1秒只能出現在4,5,6這三個位置,需要將dp[1][4],dp[1][5],dp[1][6]初始化一下,方便下面的操作。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int INF=0x3f3f3f3f;
int num[maxn][15],dp[maxn][15];
int main()
{
    int n;
    while(~scanf("%d",&n) && n)
    {
        memset(num,0,sizeof(num));
        memset(dp,0,sizeof(dp));
        int T=-INF,ans=-INF;
        for(int i=0;i<n;i++)
        {
            int pos,t;
            scanf("%d%d",&pos,&t);
            T=max(T,t);
            num[t][pos]++;
        }
        dp[1][5]=num[1][5];
        dp[1][4]=num[1][4];
        dp[1][6]=num[1][6];
        for(int i=2;i<=T;i++)
        {
            for(int j=0;j<11;j++)
            {
                if(j==0)dp[i][j]=max(dp[i-1][j],dp[i-1][j+1]);
                else if(j==10)dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]);
                else dp[i][j]=max(dp[i-1][j],max(dp[i-1][j-1],dp[i-1][j+1]));
                dp[i][j]+=num[i][j];
            }
        }
        for(int i=0;i<11;i++)
            ans=max(ans,dp[T][i]);
        printf("%d\n",ans);
    }
}

 

HDU1024 Max Sum Plus Plus(滾動陣列)

【題意】

給一個長度為n的序列,將它分成m段,使這m段的和達到最大。

【解題思路】

設dp[i][j]表示選取第j個數字,將前j個數字分成i組的最大和。

那麼易得:dp[i][j]=max(dp[i][j-1] , max(dp[i-1][0]...dp[i-1][j-1]) )+a[j]。

dp[i][j-1]:表示與前面的數一起構成1組。

max(dp[i-1][0]...dp[i-1][j-1]):表示與前面的數斷開,自成一組。

為了不爆記憶體,需要用滾動陣列優化一下,因為所有的狀態只涉及dp[i][*]和dp[i-1][*],所以max( dp[i-1][k] ) 就是上一組 k(0....j-1) 的最大值。我們可以在每次計算dp[i][j]的時候記錄下前j個的最大值用pre陣列儲存。

【程式碼】

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int INF=0x7fffffff;
int a[maxn],pre[maxn],dp[maxn];
int main()
{
    int m,n;
    while(~scanf("%d%d",&m,&n))
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        int ans=-INF;
        memset(dp,0,sizeof(dp));
        memset(pre,0,sizeof(pre));
        for(int i=1;i<=m;i++)
        {
            ans=-INF;
            for(int j=i;j<=n;j++)//因為當前計算的是第i組,所以j最小為i
            {
                dp[j]=max(dp[j-1],pre[j-1])+a[j];
                pre[j-1]=ans;
                ans=max(ans,dp[j]);
            }
        }
        printf("%d\n",ans);
    }
}