【题目整理】基础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);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39826163/article/details/84249976