poj 1742 Coins(多重背包)

http://poj.org/problem?id=1742

多重背包的题目:才开始不知道怎么来统计最后可以给出多少种价格,因为以前的形式都是给出c[i],w[i],b[i]的这里没有给出,后来自己yy了一下,利用多粗冲背包向01背包转换的第一种方法后最后从1到V里所有出现的不同的金钱数量不就是最终可以给出的各种价格吗。01背包转换的第一种方法o(n^3)肯定会TLE代码如下:

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int max_s = 107;
int f[100007],w[max_s],b[max_s];
int main()
{
int n,V,i,j,k;
while(scanf("%d%d",&n,&V))
{
if(!n&&!V) break;
for(i=0;i<n;i++)
scanf("%d",&w[i]);
for(i=0;i<n;i++)
scanf("%d",&b[i]);
memset(f,0,sizeof(f));
f[0]=0;
for(i=0;i<n;i++)
{
for(j=1;j<=b[i];j++)
{
for(k=V;k>=w[i];k--)
f[k]=max(f[k],f[k-w[i]]+w[i]);
}
}
/*for(i=V;i>=0;i--)
printf("%d ",f[i]);
printf("\n");
*/
int ans=1;
for(i=V-1;i>=0;i--)
{
if(f[i]!=f[i+1]&&f[i])
ans++;
}
cout<<ans<<endl;
}
return 0;
}

最后想到了向01背包转化的第二种办法,可是自己写的还是会超时,搜了一下解题报考,发现有人能过用这个o(n*Σlog b[i])的方法,优化了自己的代码,可是还是不过贡献了N次wa最后对折人家的代码检查,只是改了一下int f[]--->到bool f[] 2594s险过。。。bool占用一个字节,应该是处理的时候比int快,,,

View Code
#include <cstdio>
using namespace std;
const int max_s = 107;
bool f[100007];
int w[max_s],b[max_s];
int V;
void zb(int c)
{
for(int i=V;i>=c;--i)
f[i]|=f[i-c];//直接或运算用0,1表示此价格是否出现过
}
void cb(int c)
{
for(int i=c;i<=V;++i)
f[i]|=f[i-c];
}
int main()
{
int n,i,k,ans;
while(scanf("%d%d",&n,&V),n+V)
{
for(i=0;i<n;++i)
scanf("%d",&w[i]);
for(i=0;i<n;++i)
scanf("%d",&b[i]);
for(i=f[0]=1;i<=V;++i) f[i]=0;
for(i=0;i<n;++i)
{
if(w[i]*b[i]>=V)
cb(w[i]);
else
{
k=1;
while(k<b[i])
{
zb(k*w[i]);
b[i]-=k;
k<<=1;
}
zb(b[i]*w[i]);
}
}
ans=0;
for(i=1;i<=V;++i)
ans+=f[i];
printf("%d\n",ans);
}
return 0;
}

最后是楼教主的o(n*v)的方法。。ORZ啊。。

1284s A

View Code
#include <cstdio>
#include <iostream>
using namespace std;
const int max_s = 107;
int f[100007],w[max_s],b[max_s];
//f[i]来表示当前i价格是否出现过,
int sum[100007];//当价格达到i时,最多使用这一种硬币的次数
int main()
{
int n,V,i,j;
while(scanf("%d%d",&n,&V),n+V)
{
int ans=0;
for(i=0;i<n;i++)
scanf("%d",&w[i]);
for(i=0;i<n;i++)
scanf("%d",&b[i]);
for(i=f[0]=1;i<=V;i++) f[i]=0;
for(i=0;i<n;i++)
{
for(j=0;j<=V;j++) sum[j]=0;//关键是用sum来限定了次数
for(j=w[i];j<=V;j++)//从w[i]到V循环检查看是否能够出现前边没有出现的价格
{
if(!f[j]&&f[j-w[i]]&&sum[j-w[i]]<b[i])
//如果j价格没有出现过,且j-w[i]出现过,并且使用i硬币的次数没有超出给定的数量
{
f[j]=1;//标记为已出现过
sum[j]=sum[j-w[i]]+1;//使用次数+1
ans++;//满足条件++
}
}
}
cout<<ans<<endl;
}
}





posted @ 2011-12-08 16:42  E_star  阅读(761)  评论(0编辑  收藏  举报