HDU 2844 Coins 背包问题 + 二进制优化
题目大意:某个人有n种硬币,每种硬币价值为v,数量为c,问在总价值不超过m的条件下,最多有多少种组合方式。
题目思路:
1.对于某种硬币 如果v*c 大于 m,就意味着无论取多少枚硬币,只要总价值不大于m就取不完该种硬币--完全背包。
2.如果某种硬币,如果v*c 不大于m,就意味着这是多重背包,因此可以用二进制方法优化一下。
3.对于不大于m的任意数字j,dp[j]=0代表无法组合成j,dp[j]=1 代表可以组合成j,dp[j]=dp[j]|dp[j-v[i]*k](k为选择第i种硬币的数量)。
二进制优化:1,2,4,8……可以组合成任意数,将十进制数字1,2,4,8……转化成二进制
1
10
11
100
……
我们可以发现1,2,4,8……等数字的二进制每一位上都可以组合形成0或1,因此可以组合成其他任意数字。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<stdio.h> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<string> #include<cstring> #include<vector> #include<queue> #define INF 0x3f3f3f3f #define MAX 1000005 using namespace std; int dp[MAX],v[MAX],c[MAX],n,m; int main() { int i,j,k; while(scanf("%d%d",&n,&m),n+m) { for(i=1;i<=n;i++) scanf("%d",&v[i]); for(i=1;i<=n;i++) scanf("%d",&c[i]); memset(dp,0,sizeof(dp)); dp[0]=1; for(i=1;i<=n;i++) { if(c[i]*v[i] >= m) { for(j=v[i];j<=m;j++)//完全背包 dp[j]=dp[j]|dp[j-v[i]]; } else { for(k=1;k<=c[i]/2;k*=2)//二进制优化 { for(j=m;j>=v[i]*k;j--)//多重背包 { dp[j]=dp[j]|dp[j-v[i]*k]; } } k=c[i]-k+1; for(j=m;j>=v[i]*k;j--) dp[j]=dp[j]|dp[j-v[i]*k]; } } int ans=0; for(i=1;i<=m;i++) if(dp[i]) ans++; printf("%d\n",ans); } return 0; }