二进制优化
https://vjudge.net/problem/POJ-1742
题面:有n中面额的钱,每种有ci个,问1--m里有多少个数能用这些钱组成。
1 int n,m,a[105],num[105],dp[100005]; 2 void comdp(int w,int v) 3 { 4 int i; 5 for(i=w; i<=m; i++) 6 dp[i]=max(dp[i],dp[i-w]+v); 7 } 8 void zeroone(int w,int v) 9 { 10 int i; 11 for(i=m; i>=w; i--) 12 dp[i]=max(dp[i],dp[i-w]+v); 13 } 14 void multidp(int w,int v,int cnt)//此时开始多重背包,dp[i]表示背包中重量为i时所包含的最大价值 15 { 16 if(cnt*w>=m)//此时相当于物品数量无限进行完全背包 17 { 18 comdp(w,v); 19 return; 20 } 21 int k=1;//否则进行01背包转化,具体由代码下数学定理可得 22 while(k<=cnt) 23 { 24 zeroone(k*w,k*v); 25 cnt-=k; 26 k*=2; 27 } 28 zeroone(cnt*w,cnt*v); 29 return ; 30 } 31 int main() 32 { 33 while(~scanf("%d%d", &n, &m)){ 34 if(!n&&!m) break; 35 for(int i = 0; i <= m; i++){ 36 dp[i] = -INF; 37 } 38 dp[0]=0; 39 for(int i = 0; i < n; i++){ 40 scanf("%d", &a[i]); 41 } 42 for(int i = 0; i < n; i++){ 43 scanf("%d", &num[i]); 44 } 45 for(int i = 0; i < n; i++) 46 multidp(a[i], a[i], num[i]); 47 int ans=0; 48 for(int i = 1; i <= m; i++){ 49 if(dp[i]>0) 50 ans++; 51 } 52 printf("%d ", ans); 53 } 54 return 0; 55 }
这里dp要换成bool数组表示能否组成的状态,节省时间。
1 bool dp[MAXN]; 2 int V; 3 P p[MAXN]; 4 void zero(int cost) 5 { 6 for(int i=V;i>=cost;i--) 7 dp[i] |= dp[i-cost]; 8 } 9 void complet(int cost) 10 { 11 for(int i=cost;i<=V;i++) 12 dp[i] |= dp[i-cost]; 13 } 14 void multi(int cost, int amount) 15 { 16 if(cost*amount>=V){ 17 complet(cost); 18 return; 19 } 20 int k=1; 21 while(k<amount){ 22 zero(k*cost); 23 amount-=k; 24 k=k*2; 25 } 26 zero(amount*cost); 27 } 28 //多重背包的二进制优化 V-背包总容量 cost-单件物品花费(重量) amount-单件物品数量 weight-单件物品价值 29 int main() 30 { 31 int n, m; 32 while(scanf("%d %d", &n, &m), n + m) 33 { 34 V = m; 35 memset(dp, 0, sizeof(dp)); 36 for(int i = 0; i < n; i++) 37 scanf("%d", &p[i].first); 38 for(int i = 0; i < n; i++) 39 scanf("%d", &p[i].second); 40 //sort(p, p + n); 41 dp[0] = 1; 42 for(int i = 0; i < n; i++){ 43 multi(p[i].first, p[i].second); 44 } 45 int ans = 0; 46 for(int i = 1; i <= m; i++){ 47 ans += dp[i]; 48 } 49 printf("%d ", ans); 50 } 51 return 0; 52 }