POJ

来源:互联网 发布:iphone7专业拍照软件 编辑:程序博客网 时间:2024/06/01 21:15

传送门:POJ 1742

题意:有n中面额的钱,每种有ci个,问1--m里有多少个数能用这些钱组成。

思路:一看就知道是多重背包,不过是重量等于价值的特殊形态而已,并且数据量明显不能直接暴力,这里有两种优化方法,一是二进制优化,不过这里dp要换成bool数组表示能否组成的状态,要按普通背包写的话可能会t。

二是单调队列优化,这个题普通的单调队列优化也是过不了的,在单调队列的基础上还要特判出01背包和完全背包,并且运用单调队列的部分也要相应变形,由于我们只需要表示能否组成的状态就好了,因此我们可以用一个sum表示队列中的元素和,这样可以减少入队出队操作。

二进制优化代码:

#include<iostream>#include<stdio.h>#include<algorithm>#include<string.h>using namespace std;typedef pair<int,int>P;const int MAXN=100010;int gcd(int a,int b){return b?gcd(b,a%b):a;}bool dp[MAXN];int V;P p[MAXN];void zero(int cost){      for(int i=V;i>=cost;i--)      dp[i] |= dp[i-cost];}  void complet(int cost)  {      for(int i=cost;i<=V;i++)      dp[i] |= dp[i-cost];  }  void multi(int cost, int amount)  {      if(cost*amount>=V)      {          complet(cost);          return;      }         int k=1;         while(k<amount)         {          zero(k*cost);        amount-=k;          k=k*2;      }      zero(amount*cost);}  //多重背包的二进制优化  V-背包总容量 cost-单件物品花费(重量)  amount-单件物品数量  weight-单件物品价值  int main(){int n, m;while(scanf("%d %d", &n, &m), n + m){V = m;memset(dp, 0, sizeof(dp));for(int i = 0; i < n; i++)scanf("%d", &p[i].first);for(int i = 0; i < n; i++)scanf("%d", &p[i].second);//sort(p, p + n);dp[0] = 1;for(int i = 0; i < n; i++){multi(p[i].first, p[i].second);}int ans = 0;for(int i = 1; i <= m; i++){ans += dp[i];}printf("%d\n", ans);} return 0;}

单调队列优化代码戳这里。

原创粉丝点击