zoukankan      html  css  js  c++  java
  • 背包系列练习及总结(hud 2602 && hdu 2844 Coins && hdu 2159 && poj 1170 Shopping Offers && hdu 3092 Least common multiple && poj 1015 Jury Compromise)

    作为一个oier,以及大学acm党背包是必不可少的一部分。好久没做背包类动规了。久违地练习下-。-

    dd__engi的背包九讲:http://love-oriented.com/pack/

    鸣谢http://blog.csdn.net/eagle_or_snail/article/details/50987044,这里有大部分比较有趣的dp练手题。

    hud 2602 01背包板子题

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define clr(x) memset(x,0,sizeof(x))
     5 using namespace std;
     6 struct node
     7 {
     8     int cost,val;
     9 }bag[1010];
    10 int dp[1010];
    11 int main()
    12 {
    13     int T,n,m;
    14     scanf("%d",&T);
    15     while(T--)
    16     {
    17         scanf("%d%d",&n,&m);
    18         clr(dp);
    19         clr(bag);
    20         for(int i=1;i<=n;i++)
    21             scanf("%d",&bag[i].val);
    22         for(int i=1;i<=n;i++)
    23             scanf("%d",&bag[i].cost);
    24         for(int i=1;i<=n;i++)
    25             for(int j=m;j>=bag[i].cost;j--)
    26             {
    27                 if(dp[j]<dp[j-bag[i].cost]+bag[i].val)
    28                     dp[j]=dp[j-bag[i].cost]+bag[i].val;
    29             }
    30         printf("%d
    ",dp[m]);
    31     }
    32     return 0;
    33 }
    01背包

    hdu 2844 Coins 多重背包

    就是一个10w的多重背包,每个物品的cost同时也作为value去做背包,我们求的是每个容量下的价值,所以没法做背包九讲里提到的对最终目标的常数优化。那么我们做完以后,统计一下背包里dp[i]==i的空间i总数即为答案。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define clr(x) memset(x,0,sizeof(x))
     5 #define MAXN 100010
     6 using namespace std;
     7 int dp[MAXN];
     8 struct node
     9 {
    10     int num,cost;
    11 }good[MAXN];
    12 int n,m,s,t,l,k,ans;
    13 int max(int a,int b)
    14 {
    15     return a>b?a:b;
    16 }
    17 void zeroonepack(int cost,int val)
    18 {
    19     for(int i=m;i>=cost;i--)
    20     {
    21         dp[i]=max(dp[i],dp[i-cost]+val);
    22     }
    23     return ;
    24 }
    25 void completepack(int cost,int val)
    26 {
    27     for(int i=cost;i<=m;i++)
    28     {
    29         dp[i]=max(dp[i],dp[i-cost]+val);
    30     }
    31     return ;
    32 }
    33 void multipack(int cost,int val,int num)
    34 {
    35     if(cost*num>=m)
    36     {
    37         completepack(cost,val);
    38     }
    39     else
    40     {
    41         int t=1;
    42         while(t<=num)
    43         {
    44             zeroonepack(t*cost,t*val);
    45             num-=t;
    46             t*=2;
    47         }
    48         zeroonepack(num*cost,num*val);
    49     }
    50     return ;
    51 }
    52 int main()
    53 {
    54     while(scanf("%d%d",&n,&m)!=EOF && m!=0 && n!=0)
    55     {
    56         clr(dp);
    57         for(int i=1;i<=n;i++)
    58         {
    59             scanf("%d",&good[i].cost);
    60         }
    61         for(int i=1;i<=n;i++)
    62         {
    63             scanf("%d",&good[i].num);
    64         }
    65         for(int i=1;i<=n;i++)
    66         {
    67             multipack(good[i].cost,good[i].cost,good[i].num);
    68         }
    69         ans=0;
    70         for(int i=1;i<=m;i++)
    71         {
    72             if(dp[i]==i)
    73                 ans++;
    74         }
    75         printf("%d
    ",ans);
    76     }
    77     return 0;
    78 }
    多重背包

    hdu 2159 FATE 完全背包

    一个二维完全背包,将消耗忍耐度和杀怪数作为二维的cost,然后将求解每个忍耐度和杀怪数下经验值的最大值作为最大价值。我们做一遍二维完全背包,然后找杀怪数满(即dp[s][i])时最小的经验值超过升级所需经验值n的i,这个i即为最小忍耐度,然后m-i即为答案。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define clr(x) memset(x,0,sizeof(x))
     5 #define MAXN 110
     6 using namespace std;
     7 int dp[MAXN][MAXN],v[MAXN],c[MAXN];
     8 int n,m,k,s,t,l,ans,minn;
     9 int max(int a,int b)
    10 {
    11     return a>b?a:b;
    12 }
    13 int main()
    14 {
    15     while(scanf("%d%d%d%d",&n,&m,&k,&s)!=EOF)
    16     {
    17         clr(dp);
    18         for(int i=1;i<=k;i++)
    19         {
    20             scanf("%d%d",&v[i],&c[i]);
    21         }
    22         for(int i=1;i<=k;i++)
    23         {
    24             for(int j=1;j<=s;j++)
    25             {
    26                 for(int t=c[i];t<=m;t++)
    27                     dp[j][t]=max(dp[j][t],max(dp[j-1][t],dp[j-1][t-c[i]]+v[i]));
    28             }
    29         }
    30         if(dp[s][m]<n)
    31         {
    32             printf("-1
    ");
    33         }
    34         else
    35         {
    36                 minn=m;
    37                 for(int i=0;i<=m;i++)
    38                     if(dp[s][i]>=n && i<minn)
    39                     {
    40                         minn=i;
    41                         break;
    42                     }
    43                 printf("%d
    ",m-minn);
    44         }
    45     }
    46     return 0;
    47 }
    二维完全背包

    poj 1170 Shopping Offers 状压+完全背包

    最多五个物品,每个物品最多五件。因此可以将每个状态,cost,最终状态压缩成五位的六进制数,然后做求最小值的完全背包就行了。当然这题还是用编号的形式给出物品的,你需要把物品弄成连续的编号再做压缩操作。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define clr(x) memset(x,0,sizeof(x))
     5 #define clrmax(x) memset(x,0x3f3f3f3f,sizeof(x))
     6 #define MAXN 1000010
     7 using namespace std;
     8 int dp[MAXN];
     9 int v[MAXN],c[MAXN];
    10 int inf[MAXN];
    11 int bit(int n)
    12 {
    13     int l=1;
    14     n--;
    15     while(n--)
    16         l*=6;
    17     return l;
    18 }
    19 bool infer(int i,int j)
    20 {
    21     while(j)
    22     {
    23         if(i%6<j%6)
    24             return false;
    25         i/=6;
    26         j/=6;
    27     }
    28     return true;
    29 }
    30 int main()
    31 {
    32     int n,m,maxn,ans,t,k,l,r,cnt,flag,num;
    33     while(scanf("%d",&n)!=EOF)
    34     {
    35         m=0;
    36         clr(inf);
    37         clrmax(dp);
    38         for(int i=1;i<=n;i++)
    39         {
    40             scanf("%d",&k);
    41             inf[k]=i;
    42             c[i]=bit(i);
    43             scanf("%d%d",&num,&v[i]);
    44             m+=bit(i)*num;
    45         }
    46         cnt=n;
    47         scanf("%d",&k);
    48         for(int i=1;i<=k;i++)
    49         {
    50             scanf("%d",&l);
    51             flag=0;
    52             r=0;
    53             for(int i=1;i<=l;i++)
    54             {
    55                 scanf("%d%d",&n,&t);
    56                 if(inf[n]==0 || flag==1)
    57                 {
    58                     flag=1;
    59                     continue;
    60                 }
    61                 r+=bit(inf[n])*t;
    62             }
    63             if(flag==0)
    64             {
    65                 c[++cnt]=r;
    66                 scanf("%d",&v[cnt]);
    67             }
    68             else
    69             {
    70                 scanf("%d",&r);
    71             }
    72         }
    73         dp[0]=0;
    74         for(int i=1;i<=cnt;i++)
    75         {
    76             for(int j=c[i];j<=m;j++)
    77             if(infer(j,c[i]))
    78             {
    79                 dp[j]=min(dp[j],dp[j-c[i]]+v[i]);
    80             }
    81         }
    82         printf("%d
    ",dp[m]);
    83     }
    84     return 0;
    85 }
    状压完全背包

     hdu 3092 Least common multiple 转化为01背包

    把一个数n进行划分,求最大的lcm,并将n%mod。

    我们先不要在意%mod。对于一个数n,我们发觉(只有质数或者相同质数的积和剩下一堆1)的划分的情况下n的lcm会达到最大——因为,在第p次非质数的划分你可以拆解成n个质数相乘,将n个质数或者相同质数的乘积唯一化为k个作为划分后,这k个数的和小于等于该非质数,显然后者能使第p次划分完之后剩下的划分数字更大,得出来的lcm更大。当然剩下的多余的数全部拆解成1,这样对答案是不影响的(没有贡献的)。你可以发觉这很像一个01背包,而且是最大容量为n的背包,而物品则是质数或相同质数的积,cost为该数,value也为该数,那么对于每个状态的转移为:dp[j]=max(dp[j-c[i]]*v[i],dp[j])。并且每个物品只影响一次,即为01背包,毕竟一个质数对该状态的贡献只有乘一次该质数而不能多次。所以加上一些n个相同质数的积的细节处理(想想为什么)后做个01背包,即能求解出答案 dp[n]。
     然而相乘数字太大,并且需要mod,我们可以用一个数组dp来存进行特殊处理过的大小以方便进行动态规划,另一个数组ans来存答案,包括%mod。将dp中的数全部log10以表示更大范围,每次转移就变为dp[j]=max(dp[j-c[i]]+log10(v[i]),dp[j])便于比较大小,dp[j]被更新后ans也同样更新,并且%mod。

    最后求出来ans[n]即为答案。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<cstring>
     5 #define clr(x) memset(x,0,sizeof(x))
     6 #define MAXN 10010
     7 using namespace std;
     8 double dp[MAXN];
     9 int ans[MAXN];
    10 int prime[MAXN],inf[MAXN];
    11 int n,m,s,k,t,mod,cnt;
    12 double u,v,b,x;
    13 void primer(int n)
    14 {
    15     clr(inf);
    16     cnt=0;
    17     for(int i=2;i<=n;i++)
    18     {
    19         if(!inf[i])
    20         {
    21             prime[++cnt]=i;
    22             inf[i]=1;
    23         }
    24         for(int j=1;j<=cnt;j++)
    25         {
    26             if(prime[j]*i>n) break;
    27             inf[prime[j]*i]=1;
    28             if(i%prime[j]==0)
    29                     break;
    30         }
    31     }
    32     return ;
    33 }
    34 int main()
    35 {
    36     primer(3000);
    37     while(scanf("%d%d",&n,&mod)==2)
    38     {
    39         clr(dp);
    40         for(int i=0;i<=n;i++)
    41             ans[i]=1;
    42         for(int i=1;i<=cnt && prime[i]<=n;i++)
    43         {
    44             u=log10(prime[i]*1.0);
    45             for(int j=n;j>=prime[i];j--)
    46             {
    47                 for(int k=1,t=prime[i];t<=j;k++,t=t*prime[i])
    48                     if(dp[j-t]+k*u>dp[j])
    49                 {
    50                     dp[j]=dp[j-t]+k*u;
    51                     ans[j]=(ans[j-t]*t)%mod;
    52                 }
    53             }
    54         }
    55         printf("%d
    ",ans[n]);
    56     }
    57     return 0;
    58 }
    背包

     poj 1015 Jury Compromise 区间右移+输出路径

    这一题,我们需要得出最小的$ |D[sum]-P[sum]|$的情况下最大的$ |D[sum]+P[sum]|$。

    那我们可以考虑做一个二维的01背包,其中背包的两个费用为人数和当前做到第i个人的d[sum]-p[sum]。因为这样会涉及负数,因此将整个区间右移。考虑到$ mle 20$ 以及 $ -20 le d[i]-d[j] le 20 $ 因此区间为[-400,400],将其右移为[0.800]。然后我们要求的是背包在当前费用下最大的d[sum]+p[sum]。在按照d[sum]-p[sum]这个费用循环时注意,背包上限是(400<400+c[i])?400:400+c[i]下限是c[i]-400>-400?c[i]-400:-400。

    背包处理完以后,在400附近找最靠近400的i,即最小的|d[sum]-p[sum]|,并且 dp[n][m][400+i]是最大的,然后输出(i+dp[n][m][400+i])/2为P[sum],(i-dp[n][m][400+i])/2为D[sum]。

    接下来就是按路径输出的问题了,这个路径背包九讲里有讲过怎么获得我就不再赘言了。但是poj说好的是special judge,然后发觉应该输出最大字典序才行。。坑死了,害我看了好久。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #define clr(x) memset(x,0,sizeof(x))
     5 #define clrmin(x) memset(x,-0x3f3f3f3f,sizeof(x))
     6 using namespace std;
     7 int dp[210][50][810];
     8 int c[210],v[210],ans[50];
     9 int n,m,k,s,t,l,r;
    10 int max(int a,int b)
    11 {
    12     return a>b?a:b;
    13 }
    14 int main()
    15 {
    16     int kase=0,fit;
    17     while(scanf("%d%d",&n,&m)!=EOF && n>0 && m>0)
    18     {
    19         clrmin(dp);
    20         dp[0][0][400]=0;
    21         for(int i=1;i<=n;i++)
    22         {
    23             scanf("%d%d",&l,&r);
    24             c[i]=l-r;
    25             v[i]=l+r;
    26             dp[i][0][400]=0;
    27         }
    28         for(int i=1;i<=n;i++)
    29         {
    30             for(int j=m;j>0;j--)
    31             {
    32                 for(int k=(400<400+c[i])?400:400+c[i];k>=c[i]-400 && k>=-400;k--)
    33                 {
    34                     dp[i][j][k+400]=max(dp[i-1][j][k+400],dp[i-1][j-1][k-c[i]+400]+v[i]);
    35                 }
    36             }
    37 /*                for(int ii=0;ii<=2;ii++)
    38                 {
    39                     for(int jj=398;jj<=406;jj++)
    40                         printf("%d ",dp[i][ii][jj]>=0?dp[i][ii][jj]:-1);
    41                     printf("
    ");
    42                 } */
    43         //    printf("%d
    ",dp[i][0][400]);
    44         }
    45         for(int i=0;i<=400;i++)
    46         {
    47             if(dp[n][m][400+i]>0 || dp[n][m][400-i]>0)
    48             {
    49                 if(dp[n][m][400+i]>dp[n][m][400-i])
    50                     l=i;
    51                 else
    52                     l=-i;
    53                 break;
    54             }
    55         }
    56         printf("Jury #%d
    ",++kase);
    57         printf("Best jury has value %d for prosecution and value %d for defence:
    ",(l+dp[n][m][400+l])/2,(dp[n][m][400+l]-l)/2);
    58         l=400+l;
    59         t=m;
    60         for(int i=n;i>=1;i--)
    61             if(dp[i][m][l]==dp[i-1][m-1][l-c[i]]+v[i])
    62             {
    63                 ans[m]=i;
    64                 m--;
    65                 l-=c[i];
    66                 if(m==0)
    67                     break;
    68             }
    69         for(int i=1;i<=t;i++)
    70             printf(" %d",ans[i]);
    71         printf("
    
    ");
    72     }
    73     return 0;
    74 }
    二维01背包

    做了这么些题,可以发觉背包题需要对费用很敏感,对于是精确到某个容量下的(即装满)的背包和非精确(即最多为该容量)的背包,赋初值的方式是不同的。还有要善于发掘背包的费用,费用可以是任何可以用来递推的下标。可能是多维(2以上)要擅用状态压缩使得问题简单化。

    当然对于dp都有一个共同点:数据范围特别小。注意下数据范围有可能就能发现他是一道dp甚至背包,从而快速解决。

  • 相关阅读:
    Python 函数知识点
    面向对象相关
    判断arg参数是否是可以被调用的
    利用U盘安装CentOS7系统
    简单模仿OpenGL中的栈的作用
    温故而知新我再一次学习库
    关于帧缓存的总结
    OGRE的相关工具和库
    OpenGL在Qt界面下的应用(helloworld)
    OpenGL加载Cg程序
  • 原文地址:https://www.cnblogs.com/wujiechao/p/6817053.html
Copyright © 2011-2022 走看看