背包问题总结篇

Posted MissZhou要努力

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了背包问题总结篇相关的知识,希望对你有一定的参考价值。

1.混合了贪心思想的背包入门

nefu1028暑假计划 01背包

给定工作开始时间、完成时间、给的工资,工作不能重叠,求最大收益。

一维Dp表示截止到当前时间的最大收益,但是事先要对结构体按结束时间排序,防止前一状态没有值

 

    #include<cstdio>  
    #include<iostream>  
    #include<cstring>  
    #include<algorithm>  
    using namespace std;  
    int dp[1005];  
    struct money  
    {  
         int l,r,w;  
    }a[1005];  
    int cmp(money x,money y)//排序都快忘了怎么写了= =  
    {  
         if(x.r==y.r) return x.l<y.l;  
         return x.r<y.r;  
    }  
    int main()  
    {  
       //  freopen("cin.txt","r",stdin);  
         int n,t,m;  
         scanf("%d",&t);  
      
              while(t--)  
              {  
                   memset(dp,0,sizeof(dp));  
                   scanf("%d%d",&m,&n);//假期时间m和可做的工作数n  
                   for(int i=1;i<=n;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].w);  
                   sort(a+1,a+1+n,cmp);  
                   for(int i=1;i<=n;i++)  
                   {  
                        for(int j=m;j>=a[i].r;j--)  
                        {  
                             dp[j]=max(dp[j],dp[j]-(dp[a[i].r]-dp[a[i].l-1])+a[i].w);//重点的重点   
                        }  
                   }  
                   printf("%d\\n",dp[m]);  
              }  
      
    }  


2.凑一半  裸的背包

 

 

UVA 562 Dividing coins 01背包

    #include <iostream>  
    #include<cstdio>  
    #include<cstring>  
    #include<cmath>  
    #include<cstdlib>  
    using namespace std;  
    int price[50500],f[50500],n,t,sum;  
    int dp()  
    {  
         memset(f,0,sizeof(f));  
         for(int i=1;i<=n;i++)  
         {  
              for(int j=sum;j>=price[i];j--)  
              {  
                   if(f[j]<f[j-price[i]]+price[i])  
                   f[j]=f[j-price[i]]+price[i];  
              }  
         }  
         return f[sum];  
    }  
    int main()  
    {  
       // freopen("cin.txt","r",stdin);  
        while(~scanf("%d",&t))  
        {  
             while(t--)  
             {  
                  scanf("%d",&n);  
                   sum=0;  
                   for(int i=1;i<=n;i++)  
                   {  
                        scanf("%d",&price[i]);  
                        sum+=price[i];  
                   }  
                   int cnt=sum;  
                   sum=(sum+1)/2;  
                   cnt=dp()*2-cnt;  
                   if(cnt<0) cnt=-cnt;  
                   printf("%d\\n",cnt);  
             }  
        }  
        return 0;  
    }  


3.概率不超过某值的最大收益

 

 

hdu2955 Robberies 01背包的变形

加法变成乘法而已

 

 

    #include <iostream>  
    #include<cstdio>  
    #include<cstring>  
    #include<cmath>  
    using namespace std;  
    double pj[1050],dp[100500],p,rate;  
    int t,m[1050],n,sum;  
    double max(double a,double b)  
    {  
         if(a<b) return b;  
         else return a;  
    }  
    int main()  
    {  
       // freopen("cin.txt","r",stdin);  
        while(~scanf("%d",&t))  
        {  
             while(t--)  
             {  
                  memset(dp,0,sizeof(dp));  
                  dp[0]=1;  
                  sum=0;  
                  scanf("%lf%d",&p,&n);  
                  p=1-p;  
                  for(int i=0;i<n;i++)  
                  {  
                       scanf("%d%lf",&m[i],&pj[i]);  
                       pj[i]=1-pj[i];  
                       sum+=m[i];  
                  }  
                  for(int i=0;i<n;i++)  
                  {  
                       for(int j=sum;j>=m[i];j--)  
                        dp[j]=max(dp[j],dp[j-m[i]]*pj[i]);  
                  }  
                  for(int i=sum;i>=0;i--)  
                  {  
                       if(dp[i]>p)  
                       {  
                            printf("%d\\n",i);  
                            break;  
                       }  
                  }  
             }  
        }  
        return 0;  
    }  


4."混合背包"

 

2015多校联合第十场hdu5410CRB and His Birthday 01背包+完全背包

完全背包中若选择某种物品选一次加一个a[i],另加b[i]的价值,只加一次。在第二层for循环中先进行一次01背包算b[i]+a[i],再进行一次多重背包算a[i]

 

 

    #include <iostream>  
    #include<cstring>  
    #include<cstdio>  
    using namespace std;  
    int dp[2005],cost[2005],a[2005],b[2005];  
    int main()  
    {  
        //freopen("cin.txt","r",stdin);  
        int t,m,n;  
        while(cin>>t)  
        {  
            while(t--)  
            {  
                cin>>m>>n;  
                memset(dp,0,sizeof(dp));  
                for(int i=0;i<n;i++) cin>>cost[i]>>a[i]>>b[i];  
                for(int i=0;i<n;i++)  
                {  
                    for(int j=m;j>=cost[i];j--)  
                        if(dp[j]<dp[j-cost[i]]+a[i]+b[i]) dp[j]=dp[j-cost[i]]+a[i]+b[i];  
                    for(int j=cost[i];j<=m;j++)  
                        if(dp[j]<dp[j-cost[i]]+a[i]) dp[j]=dp[j-cost[i]]+a[i];  
                }  
                cout<<dp[m]<<endl;  
            }  
        }  
        return 0;  
    }  


5.01背包入门题

 

 

 

 

0-1背包:nefu19采药hdu2546饭卡

    #include <iostream>  
    #include<cstring>  
    #include<cstdio>  
    #include<algorithm>  
    using namespace std;  
    int n;  
    int price[1005],f[1005];  
    int m;  
    int main()  
    {  
        //freopen("data.in.txt","r",stdin);  
        while(~scanf("%d",&n)&&n)  
        {  
             for(int i=1;i<=n;i++) scanf("%d",&price[i]);  
             scanf("%d",&m);  
             f[0]=0;  
             //for(int i=1;i<=n;i++) f[i]=-999999;  
             memset(f,0,sizeof(f));  
             sort(price+1,price+n+1);  
             for(int i=1;i<=n-1;i++)  
             {  
                  for(int v=m-5;v>=price[i];v--)  
                   f[v]=max(f[v],f[v-price[i]]+price[i]);  
             }  
      
             if(m>=5) printf("%d\\n",m-f[m-5]-price[n]);  
             else printf("%d\\n",m);  
        }  
        return 0;  
    }  

 

    #include <iostream>  
    #include<cstdio>  
    #include<cstring>  
    //#define INF 0x7ffffff  
    using namespace std;  
    int cost[1005];  
    int price[1005];  
    int f[1005];  
    int main()  
    {  
        int m,t;  
        while(~scanf("%d%d",&t,&m))  
        {  
             f[0]=0;  
             memset(f,0,sizeof(f));  
             //for(int i=1;i<=m;i++) f[i]=-999999;  
             for(int i=1;i<=m;i++) scanf("%d%d",&cost[i],&price[i]);  
             for(int i=1;i<=m;i++)  
             {  
                  for(int v=t;v>=cost[i];v--)  
                    f[v]=max(f[v],f[v-cost[i]]+price[i]);  
              }  
              printf("%d\\n",f[t]);  
        }  
        return 0;  
    }  



6.同第二题  凑一半

hdu1171big events in hdu【多重背包模板】

#include <iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
int dp[50005];  
int value[60],num[60];  
int n,total,sum;  
void zero(int cost,int weight)  
{  
    for(int i=total;i>=cost;i--) dp[i]=max(dp[i],dp[i-cost]+weight);  
}  
void complete(int cost,int weight)  
{  
    for(int i=cost;i<=cost;i++) dp[i]=max(dp[i],dp[i-cost]+weight);  
}  
void multi(int cost,int weight,int cnt)  
{  
    if(total<=cnt*cost)  
    {  
        complete(cost,weight);  
        return;  
    }  
    int k=1;  
    while(k<=cnt)  
    {  
        zero(k*cnt,k*weight);  
        cnt=cnt-k;  
        k=2*k;  
    }  
    zero(cnt*cost,cnt*weight);  
}  
int main()  
{  
    while(~scanf("%d",&n))  
    {  
        if(n==-1) break;  
        total=0;  
        sum=0;  
        for(int i=0;i<n;i++) {  
            scanf("%d%d",&value[i],&num[i]);  
            total+=(value[i]*num[i]);  
        }  
        printf("%d \\n",total);  
        sum=total;  
        total/=2;  
        memset(dp,0,sizeof(dp));  
        for(int i=0;i<n;i++)  
        {  
            multi(value[i],value[i],num[i]);  
        }  
        printf("%d %d\\n",sum-dp[total],dp[total]);  
    }  
    return 0;  
}  

也可以用01背包搞 就是把相同价值的一种看成是多件

 

 

    #include<cstdio>  
    #include<cstring>  
    #include<algorithm>  
    using namespace std;  
    int n,tot,a,b,sum;  
    int dp[255000],val[600];  
    int main()  
    {  
        while(~scanf("%d",&n),n>0)  
        {  
            sum=0;  
            tot=0;  
            for(int i=0;i<n;i++)  
            {  
                scanf("%d%d",&a,&b);  
                while(b--)  
                {  
                    val[tot++]=a;  
                    sum+=a;  
                }  
            }  
            memset(dp,0,sizeof(dp));  
            for(int i=0;i<tot;i++)  
            {  
                for(int j=sum/2;j>=val[i];j--)  
                {  
                    dp[j]=max(dp[j],dp[j-val[i]]+val[i]);  
                }  
            }  
            printf("%d %d\\n",sum-dp[sum/2],dp[sum/2]);  
        }  
        return 0;  
    }  


7.  01背包求第k优解

 

hdu2639bone collector II【第K优解】

作为一个正常的背包,我们必须做的两重循环是一定要写的,接下来就是维护最优解到第k优解的的数组。个人觉得这个题最最值得学习的就是维护数组的写法:

首先每次想加入新物品的时候,正常来说我们有两个值:之前的dp和加入新物品时的值,然而我们现在也需要考虑第k优解的问题,所以用两个临时的数组倒一下,再用这两个数组合并到一起存储到最终的dp数组中

 

    /************ 
    hdu2639 
    2015.10.20 
    109MS 5548K 1226B 
    ************/  
    #include <iostream>  
    #include<cstdio>  
    #include<cstring>  
    #include<algorithm>  
    using namespace std;  
    int dp[1009][1009];  
    int value[109],cost[109];  
    int n,k,v,t;  
    int a[1009],b[1009];  
    int main()  
    {  
        while(~scanf("%d",&t))  
        {  
            while(t--)  
            {  
                scanf("%d%d%d",&n,&v,&k);  
                for(int i=1;i<=n;i++) scanf("%d",&value[i]);  
                for(int i=1;i<=n;i++) scanf("%d",&cost[i]);  
                memset(dp,0,sizeof(dp));  
                for(int i=1;i<=n;i++)  
                    for(int j=v;j>=cost[i];j--)  
                    {  
                        for(int m=1;m<=k;m++)  
                        {  
                            a[m]=dp[j][m];  
                            b[m]=dp[j-cost[i]][m]+value[i];  
                        }  
                        int x=1,y=1,w=1;  
                        a[k+1]=-1;b[k+1]=-1;  
                        while((w<=k)&&(x<=k||y<=k))  
                        {  
                            if(a[x]<b[y]) dp[j][w]=b[y++];  
                            else dp[j][w]=a[x++];  
                            if(w==1||dp[j][w-1]!=dp[j][w]) w++;  
                        }  
                    }  
                printf("%d\\n",dp[v][k]);  
            }  
        }  
        return 0;  
    }  


8.

hdu3466Proud Merchants【至少需要Qi才能买Pi】