DP有关模板

三角塔式:

The Triangle

#include <iostream>      
#include <algorithm>     
using namespace std;    
     
#define MAX 101    
      
int D[MAX][MAX];        
int n;      
int maxSum[MAX][MAX];    
     
int MaxSum(int i, int j){          
    if( maxSum[i][j] != -1 )             
        return maxSum[i][j];          
    if(i==n)       
        maxSum[i][j] = D[i][j];         
    else{        
        int x = MaxSum(i+1,j);           
        int y = MaxSum(i+1,j+1);           
        maxSum[i][j] = max(x,y)+ D[i][j];         
    }         
    return maxSum[i][j];     
}     
int main(){        
    int i,j;        
    cin >> n;        
    for(i=1;i<=n;i++)       
        for(j=1;j<=i;j++) {           
            cin >> D[i][j];           
            maxSum[i][j] = -1;       
        }        
    cout << MaxSum(1,1) << endl;     
}  

最长字段和:

Max Sum

#include<string.h>
#include<stdio.h>     
int main()  
{  
    int t,n,a[100005];  
    int T=0;  
    while(scanf("%d",&t)!=-1)  
      while(t--)  
      {  
          T++;  
          scanf("%d",&n);   
          for(int i=1;i<=n;i++)  
            scanf("%d",&a[i]);  
          int head=1,tail=1,x=1;  
          int max=a[1],sum=a[1];  
          for(int i=2;i<=n;i++)  
          {  
              if(sum+a[i]<a[i])  
              {  
                  x=i;  
                  sum=a[i];  
              }  
              else  
              {  
                  sum+=a[i];  
              }  
              if(sum>max)  
              {  
                  max=sum;  
                  tail=i;  
                  head=x;  
              }  
  
          }  
          printf("Case %d:\n%d %d %d\n",T,max,head,tail);  
          if(t) printf("\n");  
      }  
      return 0;  
}  

最大m子段和:

Max Sum Plus Plus

#include <iostream>  
#include <cstdio>  
#include <cstring>  
  
using namespace std;  
  
#define N 1000000  
#define INF 0x7fffffff  
  
int a[N+10];  
int dp[N+10],Max[N+10];//max( dp[i-1][k] ) 就是上一组 0....j-1 的最大值。  
  
int main()  
{  
    int n,m,mmax;  
    while (~scanf("%d%d",&m,&n))  
    {  
        for (int i=1;i<=n;i++)  
        {  
            scanf("%d",&a[i]);  
        }  
        memset(dp,0,sizeof(dp));  
        memset(Max,0,sizeof(Max));  
        for (int i=1;i<=m;i++)//分成几组  
        {  
            mmax=-INF;  
            for (int j=i;j<=n;j++)//j个数分成i组,至少要有i个数  
            {  
                dp[j]=max(dp[j-1]+a[j],Max[j-1]+a[j]);  
                Max[j-1]=mmax;  
                mmax=max(mmax,dp[j]);  
            }  
        }  
        printf ("%d\n",mmax);  
    }  
    return 0;  
}  

最长上升子序:

Constructing Roads In JGShining’s Kingdom

#include<cstdio>
#include<algorithm>
const int MAXN=200001;

int a[MAXN];
int d[MAXN];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    d[1]=a[1];
    int len=1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]>d[len])
            d[++len]=a[i];
        else
        {
            int j=std::lower_bound(d+1,d+len+1,a[i])-d;
            d[j]=a[i]; 
        }
    }
    printf("%d\n",len);    
    return 0;
}

Common Subsequence

#include<bits/stdc++.h>  
using namespace std;  
char a[505],b[505];  
int dp[505][505];  
int main()  
{  
    while(~scanf("%s%s",a,b))  
    {  
        int len1=strlen(a);  
        int len2=strlen(b);  
        for(int i=0;i<len1;i++)  
            dp[0][i]=0;  
        for(int i=0;i<len2;i++)  
            dp[i][0]=0;  
        for(int i=1;i<=len1;i++)  
        {  
            for(int j=1;j<=len2;j++)  
            {  
                if(a[i-1]==b[j-1])  
                    dp[i][j]=dp[i-1][j-1]+1;  
                else  
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);  
            }  
        }  
        printf("%d\n",dp[len1][len2]);  
    }  
}

揹包问题——(
本文作者frankchenfu,blogs网址http://www.cnblogs.com/frankchenfu/,转载请保留此文字。)

1.01揹包:有n种物品与承重为m的揹包。每种物品只有一件,每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大。

2.完全揹包:有n种物品与承重为m的揹包。每种物品有无限多件,每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大。

3.多重揹包:有n种物品与承重为m的揹包。每种物品有有限件num[i],每个物品都有对应的重量weight[i]与价值value[i],求解如何装包使得价值最大。

1.01揹包

 ①、确认子问题和状态 
  01揹包问题需要求解的就是,为了体积V的揹包中物体总价值最大化,N件物品中第i件应该放入揹包中吗?(其中每个物品最多只能放一件)
  为此,我们定义一个二维数组,其中每个元素代表一个状态,即前i个物体中若干个放入体积为V揹包中最大价值。数组为:f[N][V],其中fij表示前i件中若干个物品放入体积为j的揹包中的最大价值。 

   ②、初始状态

初始状态为f[0][0-V]和f[0-n][0]都为0.

前者表示前0个物品(也就是空物品)无论装入多大的包中总价值都为0,后者表示体积为0的揹包啥价值的物品都装不进去。

   ③、转移函数

if (揹包体积j小于物品i的体积)
    f[i][j] = f[i-1][j] //揹包装不下第i个物体,目前只能靠前i-1个物体装包
else
    f[i][j] = max(f[i-1][j], f[i-1][j-Vi] + Wi)

(1). j < w[i] 的情况,这时候揹包容量不足以放下第 i 件物品,只能选择不拿

m[ i ][ j ] = m[ i-1 ][ j ]

(2). j>=w[i] 的情况,这时揹包容量可以放下第 i 件物品,我们就要考虑拿这件物品是否能获取更大的价值。

如果拿取,m[ i ][ j ]=m[ i-1 ][ j-w[ i ] ] + v[ i ]。 这里的m[ i-1 ][ j-w[ i ] ]指的就是考虑了i-1件物品,揹包容量为j-w[i]时的最大价值,也是相当于为第i件物品腾出了w[i]的空间。

如果不拿,m[ i ][ j ] = m[ i-1 ][ j ] , 同(1)

究竟是拿还是不拿,自然是比较这两种情况那种价值最大。

/*
01揹包
适用于输入格式如下的问题,出现问题请自行调整:
第一行两个整数M, N分别表示揹包空间与物品总数;
第2到N+1行每行两个整数,分别表示这类物品每个的价值和所需空间。 
*/
#include<cstdio>
const int MAXM=10001,MAXN=51;
int m,n;
int w[MAXN],c[MAXN];
int f[MAXM];
int max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&w[i],&c[i]);
    for(int i=1;i<=n;i++)
        for(int j=m;j>=w[i];j--)
            f[j]=max(f[j],f[j-w[i]]+c[i]);
    printf("%d\n",f[m]);
    return 0;
}

2.完全揹包

完全揹包和01揹包类似,只不过每种物品有无限多件仅仅是for循环的顺序从逆序变成了顺序。

/*
完全揹包,输入规则:
第一行两个整数M, N分别表示揹包空间与物品总数;
第2到N+1行每行两个整数,分别表示这类物品每个的价值和所需空间。 
*/
#include<cstdio>
const int MAXM=10001,MAXN=51;
int m,n;
int w[MAXN],c[MAXN];
int f[MAXM];
int max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&w[i],&c[i]);
    for(int i=1;i<=n;i++)
        for(int j=w[i];j<=m;j++)
            f[j]=max(f[j],f[j-w[i]]+c[i]);
    printf("%d\n",f[m]);
    return 0;
}

3.多重揹包

首先这种可以把物品拆开,把相同的num[i]件物品 看成 价值跟重量相同的num[i]件不同的物品,就转化成了一个规模稍微大一点的01揹包了。

/*
多重揹包,输入格式: 
第一行,一个整数n,物品数量;
第二行,n个整数,第i个整数表示第i个物品的价格bi;
第三行,n个整数,第i个整数表示第i个物品的数量ci;
第四行,一个整数m,揹包空间。
*/
#include<cstdio>
const int MAXM=10001,MAXN=6001;
int v[MAXM],w[MAXM];
int f[MAXN];
int n,m,p;
int max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int x,y,s,t=1;
        scanf("%d%d%d",&x,&y,&s);
        for(;s>=t;t<<=1)//二进制思想
        {
            v[++p]=x*t;
            w[p]=y*t;
            s-=t;
        }
        v[++p]=x*s;
        w[p]=y*s;
    }
    for(int i=1;i<=p;i++)
        for(int j=m;j>=v[i];j--)
            f[j]=max(f[j],f[j-v[i]]+w[i]);
    printf("%d\n",f[m]);
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
int dp[1005];
int weight[1005],value[1005],num[1005];
int main()
{
    int n,m;
    cin>>n>>m;
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=n; i++)
        cin>>weight[i]>>value[i]>>num[i];
        
    for(int i=1; i<=n; i++)//每种物品
        
        for(int k=0; k<num[i]; k++)//其实就是把这类物品展开,调用num[i]次01揹包代码
        
            for(int j=m; j>=weight[i]; j--)//正常的01揹包代码
            
                dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
    
    cout<<dp[m]<<endl;
    return 0;
}

点赞