hdu 2844 coins (多重背包+二进制优化)
2015-10-04 14:57
351 查看
给出n种面值的硬币,以及它们的数量,问能组成不超过m的面值有多少个。
样例2解释:
面值为1的硬币2个,面值为4的硬币1个。能组成的面值有1、2、4、5、6。其中不超过面值5的面值有4个。
典型的多重背包问题。
这里的面值既相当于背包问题中的物品体积,又相当于物品的价值。
因此dp[i]表示的是能组成的不超过i的最大面值,即dp[i]<=i,对于某种面值k,若i>k且dp[i]=k,则之前必然有dp[k]=k。
最终统计答案的时候,从1到m扫一遍,统计dp[i]=i的面值i的数量即可,这样可保证能组成的各种面值刚好只被统计一次。
关于二进制优化,其实就是利用二进制的原理对同种的物品的数量进行划分。将多个同种物品视为一个物品,其价值为原价值乘上划分的数量,体积为原体积乘上划分的数量。
然后将问题转化为0-1背包求解。相比于直接转换为0-1背包,时间复杂度由O(V*sum)优化到O(V*(log(sum)) 其中sum为所有物品的总数量。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 101
#define maxV 100005
int v[maxn],num[maxn];
int dp[maxV];
inline void zero(int vol,int val,int W)
{
for(int i=W;i>=vol;--i)
dp[i]=max(dp[i],dp[i-vol]+val);
}
inline void complet(int vol,int val,int W)
{
for(int i=vol;i<=W;++i)
dp[i]=max(dp[i],dp[i-vol]+val);
}
void multi(int vol,int val,int num,int W)
{
if(vol*num>=W){
complet(vol,val,W);
return;
}
for(int i=1;i<=num;i<<=1){
zero(i*vol,i*val,W);
num-=i;
}
if(num) zero(num*vol,num*val,W);
}
int main()
{
int n,W,i;
while(scanf("%d%d",&n,&W)!=EOF&&(n+W))
{
memset(dp,0,sizeof(dp));
for(i=0;i<n;++i) scanf("%d",&v[i]);
for(i=0;i<n;++i) scanf("%d",&num[i]);
for(i=0;i<n;++i) multi(v[i],v[i],num[i],W);
int ans=0;
for(i=1;i<=W;++i)
if(dp[i]==i) ++ans;
printf("%d\n",ans);
}
return 0;
}
样例2解释:
2 5 1 4 2 1
面值为1的硬币2个,面值为4的硬币1个。能组成的面值有1、2、4、5、6。其中不超过面值5的面值有4个。
典型的多重背包问题。
这里的面值既相当于背包问题中的物品体积,又相当于物品的价值。
因此dp[i]表示的是能组成的不超过i的最大面值,即dp[i]<=i,对于某种面值k,若i>k且dp[i]=k,则之前必然有dp[k]=k。
最终统计答案的时候,从1到m扫一遍,统计dp[i]=i的面值i的数量即可,这样可保证能组成的各种面值刚好只被统计一次。
关于二进制优化,其实就是利用二进制的原理对同种的物品的数量进行划分。将多个同种物品视为一个物品,其价值为原价值乘上划分的数量,体积为原体积乘上划分的数量。
然后将问题转化为0-1背包求解。相比于直接转换为0-1背包,时间复杂度由O(V*sum)优化到O(V*(log(sum)) 其中sum为所有物品的总数量。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define maxn 101
#define maxV 100005
int v[maxn],num[maxn];
int dp[maxV];
inline void zero(int vol,int val,int W)
{
for(int i=W;i>=vol;--i)
dp[i]=max(dp[i],dp[i-vol]+val);
}
inline void complet(int vol,int val,int W)
{
for(int i=vol;i<=W;++i)
dp[i]=max(dp[i],dp[i-vol]+val);
}
void multi(int vol,int val,int num,int W)
{
if(vol*num>=W){
complet(vol,val,W);
return;
}
for(int i=1;i<=num;i<<=1){
zero(i*vol,i*val,W);
num-=i;
}
if(num) zero(num*vol,num*val,W);
}
int main()
{
int n,W,i;
while(scanf("%d%d",&n,&W)!=EOF&&(n+W))
{
memset(dp,0,sizeof(dp));
for(i=0;i<n;++i) scanf("%d",&v[i]);
for(i=0;i<n;++i) scanf("%d",&num[i]);
for(i=0;i<n;++i) multi(v[i],v[i],num[i],W);
int ans=0;
for(i=1;i<=W;++i)
if(dp[i]==i) ++ans;
printf("%d\n",ans);
}
return 0;
}
相关文章推荐
- 【算法设计与数据结构】URAL 1323. Classmates
- Kmeans原理及实现
- Eclipse 从安装到使用(傻子都看懂,你就不用说了,哈哈哈)
- 大哥可以写KMP该——达到strstr()
- 队列——PowerShell版
- PS滤镜— —波浪效果
- OpenCV——花环生成函数
- 为什么电脑开机后一段时间磁盘利用率达到100%
- C/C++中union关键字总结
- 116. Populating Next Right Pointers in Each Node (Tree; WFS)
- 最长子序列
- 数据结构之基础篇
- Java在一定范围随机生成日期和经纬度
- 最长子序列
- webview显示html代码(可用于新闻浏览界面)
- PHP可变参数
- Linux 2.6.25中的select系统调用 http://zhangyafeikimi.iteye.com/blog/248815
- ZooKeeper参数调优
- 工厂方法模式的两个极端
- 3*(4+6)-7 # 用代码实现这个运算表达式,仅包含逆波兰式部分,算术结果部分未写出