POJ 1742
题目:
https://vjudge.net/contest/266223#problem/F
题意:在{1, M}内凑出金额的种类。
我对于 题解 的理解:dp[i]是 有个背包容量是 i ,往里面装 各种 数量*价值看最大装入的价值
假设 dp【4】= 3,就是说,当前的 有的 硬币 条件下 人家要4元,你只能凑出3元(也可能凑出5元,但是人家不给找钱)
所以 dp【5】= 5 也就是 人家要5 ,你恰好能凑出5元。
下面的代码是偷的,最后的判断改成了 dp【i】== i ,但TLE 了。
https://blog.csdn.net/vvmame/article/details/76697198
#include<iostream>
#include<algorithm>
#include<string.h>
#define INF 0x3f3f3f3f
using namespace std;
//手表价格
int m;
//单位货币的价值
int val[1005];
//单位货币的数量
int vol[1005];
int dp[100005];
void Zero(int cost){
for(int i=m;i>=cost;i--)
dp[i]=max(dp[i],dp[i-cost]+cost);
}
//val*vol>>m,硬币可以无限使用,可以使用完全背包
void Com(int cost){
for(int i=cost;i<=m;i++)
dp[i]=max(dp[i],dp[i-cost]+cost);
}
//拆分vol,价格系数k*vol组成新硬币
void mul(int val,int vol){
if(val*vol>=m)
Com(val);
else{
int k=1;
while(k<=vol){
Zero(k*val);
vol-=k;
k*=2;
}
Zero(vol*val);
}
}
int main(){
//货币的种类
int n;
while(cin>>n>>m,(n||m)){
for(int i=1;i<=n;i++)
cin>>val[i];
for(int i=1;i<=n;i++)
cin>>vol[i];
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
mul(val[i],vol[i]);
int ans=0;
for(int i=1;i<=m;i++)
if(dp[i]==i)
ans++;
cout<<ans<<endl;
}
return 0;
}
后来听说有个可行性背包。AC了 题目要求 3000 ms 可行性背包1266ms
这也是偷的
https://blog.csdn.net/dengyaolongacmblog/article/details/25380863
f[i]的意思是 能否凑出i元,如果能 f【i】= 1;
!f[j]&&f[j-val]:如果 j 没凑出来过,并且 j-val 已经被你凑出来了。含义是:已经凑出 j-val 后,你手里还有 个 val 的硬币,刚好就凑出了 j。
used数组的意思是
有 面值 val 的硬币 6枚。
凑 j-val 元的时候 用了5个硬币,在加上 一个 面值val 的硬币,就是 刚好 6枚用完。
used[j-val]<num 保证凑 j-val 元 的时候 num枚 没有用完。
/*************************************************************************
> OS : Linux 3.2.0-60-generic #91-Ubuntu
> Author : yaolong
> Mail : dengyaolong@yeah.net
> Created Time: 2014年05月09日 星期五 10:23:35
************************************************************************/
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAXN 105
#define MAXV 100005
int cnt[MAXN],val[MAXN];
int used[MAXV];
bool f[MAXV];
int n,V;
int coins;
/*可行性背包,当问题仅仅是问能否装到,那么只要达到过就不要再做了*/
void AMultipack(int val,int num){
memset(used,0,sizeof(used));
for(int j=val;j<=V;j++){
if(!f[j]&&f[j-val]&&used[j-val]<num){
//要求f[j]没有存在过,但是f[j-val]是之前达到的,且还可以购买
f[j]=true;
used[j]=used[j-val]+1;
coins++;
}
}
}
int main(){
int n;
int i,j,k;
while(scanf("%d%d",&n,&V)&&(n||V)){
for(i=0;i<n;i++){
scanf("%d",val+i);
}
for(i=0;i<n;i++){
scanf("%d",cnt+i);
}
coins=0;
memset(f,0,sizeof(f));
f[0]=true;
for(i=0;i<n;i++){
AMultipack(val[i],cnt[i]);
}
printf("%d\n",coins);
}
return 0;
}
//我自己理解了一下 可行性背包
#include<iostream>
#include<cstring>
using namespace std;
int ans=0;
int val[105];
int num[105];
int n,m;
bool fin[100005];
int nu[100005];
void Alu(int val,int cou)
{
memset(nu,0,sizeof nu);
// nu 是关于数量的 nu[i] 是在当前凑钱的过程中 凑到了 i 元,用了 val的硬币 有多少个
for(int i=val;i<=m;i++)
if( !fin[i] && fin[i-val] && cou>nu[i-val] )
{// 如果 i 曾经凑出来过,就跳过
//没凑出来i,但是凑出来 i-val 了, 刚好你手里有 val 的硬币 (cou)枚
//cou>nu[i-val] cou 你一共有的数量,必须大于 你在凑i-val 是用掉的,
// 因为你想由 i-val 凑 i 说明至少你手里还有一个 val
fin[i]=1;
nu[i]=nu[i-val]+1;
// i-val 凑 i 成功了,那 你用掉的数量就加1。
ans++;
//能凑出来的 i (面值) 的种类就增加了。
}
}
int main()
{
while(cin>>n>>m,(n||m))
{
ans=0;
for(int i=1;i<=n;i++)
cin>>val[i];
for(int i=1;i<=n;i++)
cin>>num[i];
memset(fin,0,sizeof fin);
fin[0]=1;
// fin[i] 是看 能否凑出 i 元,如果能 fin[i]=1 否则 =0
for(int i=1;i<=n;i++)
Alu(val[i],num[i]);
// Alu(a,b) 是看 a元的硬币 有b枚,能组成什么样的 i
cout<<ans<<endl;
}
return 0;
}
注明了出处,希望大佬们不要打我。
我好毒呀。。。
有个尴尬的情况:第一个多重背包的板子,dp由int 改成 bool 就能过,飘过
题目:3000ms 我:2875ms
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,m;
int cost[105];
int cou[105];
bool dp[100005]={0};
int ans;
void comp(int costt)
{
for(int i=costt;i<=m;i++)
{
if( !dp[i] && dp[i-costt])
dp[i]=1,ans++;
}
}
void zero(int costt)
{
for(int i=m;i>=costt;i--)
if(!dp[i]&& dp[i-costt])
dp[i]=1,ans++;
}
void mul(int costt,int couu)
{
if(costt*couu>=m)
comp(costt);
else
{
int k=1;
while(k<=couu)
{
zero(costt*k);
couu-=k;
k*=2;
}
zero(couu*costt);
}
}
int main()
{
while(cin>>n>>m)
{
if(n==0&& m==0)
break;
for(int i=1;i<=n;i++)
cin>>cost[i];
for(int i=1;i<=n;i++)
cin>>cou[i];
ans=0;
memset(dp,0,sizeof dp);
dp[0]=1;
for(int i=1;i<=n;i++)
mul(cost[i],cou[i]);
cout<<ans<<endl;
}
return 0;
}
从时间来看,可行性背包NB。