<DP>总结

发布时间 2023-07-09 14:16:52作者: Bo-Wing

DP总结

常规DP

[USACO1.5] [IOI1994]数字三角形 Number Triangles

解题思路

对于到达(i,j)点时的最大值,其状态仅由(i-1,j)和(i-1,j-1)决定。

故设计dp[ i ] [ j ] = MAX(dp[ i-1 ] [ j ],dp[ i-1 ] [ j-1 ])+num[ i ] [ j ]

#include<bits/stdc++.h>

using namespace std ;

int n ;
int num[1001][1001] ;
int dp[1001][1001] ;
int main()
{
    scanf("%d",&n) ;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=i;++j)
            scanf("%d",&num[i][j]) ;
    memset(dp,0,sizeof(dp)) ;
    dp[1][1]=num[1][1] ;
    for(int i=1;i<=n-1;++i)
    {
        for(int j=1;j<=i;++j)
        {
            if(i+1<=n&&j<=i+1)
                dp[i+1][j]=max(dp[i+1][j],dp[i][j]+num[i+1][j]) ;
            if(i+1<=n&&j+1<=i+1)
                dp[i+1][j+1]=max(dp[i+1][j+1],dp[i][j]+num[i+1][j+1]) ;
        }
    }
    int ans = 0 ;
    for(int j=1;j<=n;++j) ans = max(ans,dp[n][j]) ;
    printf("%d",ans) ;
    return 0 ;
}

[NOIP1996 提高组] 挖地雷

解题思路

该题为比较简单的图上DP,注意题目要求只能从编号小的地窖走向编号较大的地窖。

设计dp[i]表示从i开始往下走能获得的最大价值。

考虑从编号最大的地窖开始倒着处理,由于题目要求输出路径,在更新DP时记录路径 即可

#include<bits/stdc++.h>

using namespace std ;

int mp[21][21],num[21],dp[21],nxt[21] ;
int n ;
int maxid , maxans=-1 ;
int main()
{
    scanf("%d",&n) ;
    for(int i=1;i<=n;++i) scanf("%d",&num[i]) ;
    for(int i=1;i<n;++i)
        for(int j=i+1;j<=n;++j)
            scanf("%d",&mp[i][j]) ;
    memset(nxt,-1,sizeof(nxt)) ;
    memset(dp,0,sizeof(dp)) ;
    dp[n] = num[n] ;
    for(int i=n-1;i>=1;--i)
    {
        dp[i]=num[i] ;
        for(int j=i+1;j<=n;++j)
        {
            if(mp[i][j]==0) continue ;
            if(dp[j]+num[i]>dp[i])
            {
                dp[i]=dp[j]+num[i] ;
                nxt[i]=j ;
            }
        }
    }
    for(int i=1;i<=n;++i)
    {
        if(dp[i]>maxans)
        {
            maxans=dp[i] ;
            maxid = i ;
        }
    }
    for(;;)
    {
        printf("%d ",maxid) ;
        maxid = nxt[maxid] ;
        if(maxid==-1) break ;
    }
    printf("\n") ;
    printf("%d",maxans) ;
    return 0 ;
}

背包问题

0-1背包

[NOIP2005 普及组] 采药

解题思路

这是最典型的0-1背包问题,背包容量+物品价值,每个物品只能用一次。

设计dp[i]表示花费i时间所能获得的最大值。

考虑一次处理每一个物品,保证每一个物品只能用一次

然后在枚举花费时间时,当i<j时,dp[i]会影响到dp[j],正正着枚举会导致同一个物品多次放入,所以应该倒着枚举。

#include<bits/stdc++.h>

using namespace std ;
int T,m ;
int value[101],tim[101] ;
int dp[1001] ;
int main()
{
    scanf("%d%d",&T,&m) ;
    for(int i=1;i<=m;++i)
        scanf("%d%d",&tim[i],&value[i]) ;
    memset(dp,0,sizeof(dp)) ;

    for(int j=1;j<=m;++j)
        for(int i=T;i>=0;--i)
            if(i-tim[j]>=0) 
                dp[i]=max(dp[i],dp[i-tim[j]]+value[j]) ;
    printf("%d",dp[T]) ;
    return 0 ;
}

区间DP

状压DP

树型DP

数位DP