基础背包问题的一些题目!!

来源:互联网 发布:双防火墙网络环境搭建 编辑:程序博客网 时间:2024/06/06 01:56

hdu1203

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1203

每件物品的cos为a[i],价值与b[i]有关,是一个简单的01背包问题。

AC代码:

#include<iostream>#include<cstdio>#include<cmath>#include<cstring>using namespace std;double dp[10005];int a[10005];double b[10005];int main(){    int n,m,i,j;    while(cin>>n>>m&&n+m)    {        for(i=0;i<m;i++)            cin>>a[i]>>b[i];        memset(dp,0,sizeof(dp));        for(i=0;i<m;i++)            for(j=n;j>=a[i];j--)                dp[j]=max(dp[j],1-(1-dp[j-a[i]])*(1-b[i]));        double res=dp[n]*100;        printf("%.1f%%\n",res);    }    return 0;}



hdu2602

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2602

典型的01背包,直接见代码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int dp[1002],i,j,num,vol;int cos[1002],wei[1002];void ZeroOnePack(int cost,int weight){    for(j=vol;j>=cost;j--)        if(dp[j]<dp[j-cost]+weight)           dp[j]=dp[j-cost]+weight;}int main(){   int tes,n;   cin>>tes;   while(tes--)   {       cin>>n>>vol;       for(i=0;i<n;i++)           scanf("%d",&cos[i]);       for(i=0;i<n;i++)           scanf("%d",&wei[i]);       memset(dp,0,sizeof(dp));       for(i=0;i<n;i++)          ZeroOnePack(wei[i],cos[i]);       cout<<dp[vol]<<endl;   }   return 0;}


hdu 1171

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1171

有n件物品,每件物品的价值和数量不同,是一个完全背包问题,由于数量并不是很大,可以将它转换为01背包问题。

AC代码:

//用两个包使得装得总量差尽量小//就找一个总量sum/2的包看最多能装多少//物品最多有n*m<=5000个.箱子开5000*50/2<2*10^5#include<iostream>#include<cstdio>#include<cstring>using namespace std;int dp[200005];int wei[5005];int main(){    int t,i,j,n,v,m,sum;    while(cin>>t&&t>0)    {        n=sum=0;        memset(dp,0,sizeof(dp));        for(i=0;i<t;i++)        {            cin>>v>>m;            sum+=v*m;            while(m--)                wei[n++]=v;        }        for(i=0;i<n;i++)            for(j=sum/2;j>=wei[i];j--)                dp[j]=max(dp[j],dp[j-wei[i]]+wei[i]);        cout<<sum-dp[sum/2]<<" "<<dp[sum/2]<<endl;    }    return 0;}


hdu1864

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1864

貌似是个背包问题,不过背包需要用到int,由于答案需要保留两位有效数字,所以将背包的cos放大100倍,把背包的容量也扩大一百倍,不过这样多少会有点精度的损失。01背包。

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>using namespace std;double wei[35],dp[3000005];int main(){    double sum,sa,sb,sc,money;    int t,n,q,i,j;    int flag;    char c;    while(cin>>sum>>t&&t)    {        n=0;        memset(dp,0,sizeof(dp));        while(t--)        {            cin>>q;            sa=sb=sc=0;  //分别记录每张发票的A,B,C价钱            flag=0;            while(q--)            {                scanf(" %c:%lf",&c,&money);                if(c<'A'||c>'C'||money>600)   //不可报销或单项物品金额>600                    flag=1;                if(c=='A') sa+=money;                else if(c=='B') sb+=money;                else if(c=='C') sc+=money;            }            if(!flag&&sa+sb+sc<=1000&&sa<=600&&sb<=600&&sc<=600)                wei[n++]=sa+sb+sc;        }        int isum=sum*100;        for(i=0;i<n;i++)        {            int iwei=wei[i]*100;            for(j=isum;j>=iwei;j--)    //同时扩大100倍                dp[j]=max(dp[j],dp[j-iwei]+wei[i]);        }        printf("%.2lf\n",dp[isum]);    }    return 0;}/*200.00 32 A:23.50 B:100.001 C:650.003 A:59.99 A:120.00 X:10.001200.00 22 B:600.00 A:400.001 C:200.501200.50 32 B:600.00 A:400.001 C:200.501 A:100.00100.00 01000 41 A:3001 B:3001 A:2001 C:500*/

hdu1712

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1712

题目说的意思是有n个作业,如果用j天去完成第i项作业,那么会得到价值wei[i][j],因此每项任务要么不完成,要么只能完成一次,是一个完全背包的问题,可以先看下背包九讲的内容P06.

for 所有的组k

    for v=V..0

        for 所有的i属于组k

            f[v]=max{f[v],f[v-c[i]]+w[i]}

意思就是说每个组里面最多只能选一项。


AC代码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int wei[105][105];int dp[105];int main(){    int n,m,i,j,k;    while(cin>>n>>m&&n+m)    {        memset(dp,0,sizeof(dp));        for(i=1;i<=n;i++)            for(j=1;j<=m;j++)                scanf("%d",&wei[i][j]);        for(i=1;i<=n;i++)   //分成的n组            for(j=m;j>=0;j--)   //总共用j天                for(k=1;k<=m;k++)   //所有的k属于组i                    if(j>=k)                        dp[j]=max(dp[j],dp[j-k]+wei[i][k]);        cout<<dp[m]<<endl;    }    return 0;}

hdu2660

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2660

题目先给你一个n,k,有n个物品,下面依次是n个物品的cos和wei,然后是背包的容量是vol,不过有个条件背包装的物品数目得不多于k,因此需要用到二维的01背包。dp[l][j]表示容量为l,最多装j个物品时候装得最大价值。状态转移方程为 dp[l][j]=max(dp[l][j],dp[l-cos[i]][j-1]+wei[i])


AC代码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int dp[1002][21],i,j,vol,k,l;int cos[1002],wei[1002];int main(){   int T;   cin>>T;   while(T--)   {       memset(dp,0,sizeof(dp));       int n;       scanf("%d%d",&n,&k);       for(i=0;i<n;i++)           scanf("%d%d",&wei[i],&cos[i]);       scanf("%d",&vol);       for(i=0;i<n;i++)          for(l=vol;l>=cos[i];l--)              for(j=1;j<=k;j++)                 dp[l][j]=max(dp[l][j],dp[l-cos[i]][j-1]+wei[i]);       printf("%d\n",dp[vol][k]);   }   return 0;}



hdu1709

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=1709

题目大意:给定几个数字,作为砝码,看哪些不能被称出。
解题思路:先01背包求出可以相加的所有的情况,找出可以被称出的重量,再暴力找出可以使用减法的情况。


AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int dp[10005];int vis[10005];int xx[10005];int res[10005];int a[105],sum,n,t;void onepack(int cost,int weight){int l;    for(l=sum;l>=cost;l--)if(dp[l]<dp[l-cost]+weight)           dp[l]=dp[l-cost]+weight;}int main(){   int i,j;   while(~scanf("%d",&n))   {  memset(dp,0,sizeof(dp));  memset(vis,0,sizeof(vis));  sum=0;      for(i=0;i<n;i++)  {  scanf("%d",&a[i]);  sum+=a[i];  }  for(i=0;i<n;i++)         onepack(a[i],a[i]);  for(i=1;i<=sum;i++)  vis[dp[i]]=1;      //先背包求出所有和能组成的  t=0;  for(i=1;i<=sum;i++)  if(vis[i])  xx[t++]=i;      //把背包求出的可行值都存放入xx数组中  for(i=0;i<t;i++)for(j=i+1;j<t;j++)vis[xx[j]-xx[i]]=1;   //用一次减法可以求出的  t=0;  for(i=1;i<=sum;i++)          if(!vis[i])     res[t++]=i;  printf("%d\n",t);  if(t>0)  {         for(i=0;i<t-1;i++) printf("%d ",res[i]); printf("%d\n",res[t-1]);  }   }   return 0;}


poj3624

题目地址:http://poj.org/problem?id=3624

标准的01背包。。。

白天断电断网,一血也没了。。

AC代码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int dp[15000];int cos[5000],wei[5000];int main(){    int i,j,n,vol;    while(cin>>n>>vol)    {        memset(dp,0,sizeof(dp));        for(i=0;i<n;i++)            cin>>cos[i]>>wei[i];        for(i=0;i<n;i++)            for(j=vol;j>=cos[i];j--)                dp[j]=max(dp[j],dp[j-cos[i]]+wei[i]);        cout<<dp[vol]<<endl;    }    return 0;}


hdu 2159

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2159

题目大意:中文题目,就不做解释了,是一个二维完全背包的标准题目。

AC代码:

#include<iostream>#include<cstring>#include<cstdio>using namespace std;int dp[105][105];int wei[105],cos[105];int main(){    int i,j,k;    int n,m,K,s;    while(cin>>n>>m>>K>>s)    {        for(i=0;i<K;i++)            scanf("%d%d",&wei[i],&cos[i]);        memset(dp,0,sizeof(dp));        for(i=0;i<K;i++)        {            for(j=1;j<=m;j++)            {                for(k=1;k<=s;k++)                {                    if(j>=cos[i])                        dp[j][k]=max(dp[j][k],dp[j-cos[i]][k-1]+wei[i]);                }            }        }        if(dp[m][s]<n)        {            puts("-1");            continue;        }        int res=m;        for(i=1;i<=m;i++)            for(j=1;j<=s;j++)            {                if(dp[i][j]>=n)                    res=min(res,i);            }        res=m-res;        cout<<res<<endl;    }    return 0;}/*10 10 1 101 110 10 1 91 19 10 2 101 12 2*/


hdu 2844&&poj  1742

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2844

多重背包,题目意思,是给n种金币的金额,金额不能超过m,然后输入每种金币的v[i],c[i]分别代表这种金币代表的金额和这种金币的数量。需要输出这些金币组合能达到不超过m的金额是多少种。

典型的二维多重背包题目。


如果采取普通的多重背包的解法,那么是O(n*m*c)
n是物品的种类数目,m是背包的容量,c是物品的每种种类相应的数目
如果应用这个题目,那么是10^7*10^3=10^10的时间复杂度,当然不可行。
当然我们可以将复杂度降到O(n*m),不过我们需要多开一个数组记录每次用物品的个数
这个题目需要求解的是能组成多少<=m的总量。
具体见代码。


#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int maxn=100005;int visi[maxn];int used[maxn];int v[105],c[105];int main(){    int n,m;    int i,j;    while(cin>>n>>m&&n+m)    {        for(i=0;i<n;i++)            cin>>v[i];        for(i=0;i<n;i++)            cin>>c[i];        int res=0;        memset(visi,0,sizeof(visi));        visi[0]=1;        for(i=0;i<n;i++)        {            memset(used,0,sizeof(used));            for(j=v[i];j<=m;j++)            {                if(!visi[j]&&visi[j-v[i]]&&used[j-v[i]]<c[i])                {                    visi[j]=1;                    used[j]=used[j-v[i]]+1;                    res++;                }            }        }        cout<<res<<endl;    }    return 0;}/*3 101 2 4 2 1 12 51 4 2 10 0*/



hdu 2191

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=2191

题目大意:给你背包的容量vol,然后给你n种物品,每种物品有它的价值,体积,当然还有每种物品的数量。典型的多重背包,由于种类最多为100,每种物品数量最多为20,如果转换为01背包总量最多为2*10^3然后再乘上背包容量100,10^5的时间复杂度,可以接受。

AC代码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int cos[2005],wei[2005];int dp[105];int main(){    int tes;    cin>>tes;    int i,j;    while(tes--)    {        int vol,m;        cin>>vol>>m;        int num=0;        int a,b,c;        for(i=0;i<m;i++)        {            cin>>a>>b>>c;            for(j=0;j<c;j++)    //多重背包转换为01背包            {                cos[num]=a;                wei[num++]=b;            }        }        memset(dp,0,sizeof(dp));        for(i=0;i<num;i++)            for(j=vol;j>=cos[i];j--)                dp[j]=max(dp[j],dp[j-cos[i]]+wei[i]);        cout<<dp[vol]<<endl;    }    return 0;}/*18 22 100 44 100 2*/


poj 2392      完全背包

题目地址:http://poj.org/problem?id=2392

题目大意:给你k种石头,每种石头有自己的高度h,和最高能呆的高度a,还有数量c,问用这些石头堆起来最高的高度。

我们需要对这些石头的最高能呆的高度从低到高排个序,然后就直接用完全背包的套路就可以了。

例如

2

8  15  1

3   8    3

排序之后最大高度是14,不排序的最大高度是8.

AC代码:

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<cmath>#include<algorithm>using namespace std;int dp[40005];int num[40005];struct node{    int h;    int a;    int c;}block[405];bool cmp(node p1,node p2){    return p1.a<p2.a;}int main(){    int k,i,j;    while(cin>>k)    {        memset(dp,0,sizeof(dp));        dp[0]=1;        for(i=0;i<k;i++)            cin>>block[i].h>>block[i].a>>block[i].c;        sort(block,block+k,cmp);        int ans=0;        for(i=0;i<k;i++)        {            memset(num,0,sizeof(num));   //记录用了多少个石头的            for(j=block[i].h;j<=block[i].a;j++)            {                if(!dp[j]&&dp[j-block[i].h]&&num[j-block[i].h]<block[i].c)                {                    dp[j]=1;                    num[j]=num[j-block[i].h]+1;                    ans=max(ans,j);                }            }        }        cout<<ans<<endl;    }    return 0;}


poj 2184经典的01背包

题目地址:http://poj.org/problem?id=2184

题目的意思是给你一些牛的s值和f值,让你在这些牛选取一些牛使得在保证s值之和>=0,f值之和>=0的基础上使得所有的s值和f值之和最大。

首先看到这个题目就会想到背包,而且是01背包,但是中间有负数,处理的方法就是采取手动的移位,所有的都往右移动即可。我们所要做的就是用dp[s]背包来背f,就是s一定的情况下使得f最大,当然我们也需要记得01背包循环的顺序,所以需要根据s[i]的正负值分类考虑循环顺序。详见代码:

AC代码:

#include<iostream>#include<cstdio>using namespace std;const int tab=1e5;int s[105],f[105];int dp[200005];int main(){    int n,i,j;    while(cin>>n)    {        for(i=0;i<n;i++)            cin>>s[i]>>f[i];        for(i=0;i<=2e5;i++)            dp[i]=-1e8;        dp[tab]=0;        int l=0,r=0;        for(i=0;i<n;i++)        {            l=min(l,l+s[i]);            r=max(r,r+s[i]);            if(s[i]<0)            {                for(j=l;j<=r;j++)                {                    dp[j+tab]=max(dp[j+tab-s[i]]+f[i],dp[j+tab]);                }            }            else            {                for(j=r;j>=l;j--)                {                    dp[j+tab]=max(dp[j+tab-s[i]]+f[i],dp[j+tab]);                }            }        }        int res=0;        for(i=0;i<=r;i++)        {            if(dp[i+tab]>=0)                res=max(res,i+dp[i+tab]);        }        cout<<res<<endl;    }    return 0;}/*5-5 78 -66 -32 1-8 -5*/


5 1