链接: https://vjudge.net/contest/176436#problem/B
题目大意:
有各种不同面值的货币,每种面值的货币有不同的数量,请找出利用这些货币可以凑成的最接近且小于等于给定的数字cash的金额。
用v*∑num【i】的算法肯定超时,因为这里v太大,num【i】也不小
超时代码:
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
int m,n;
int v[1999],num[1999],w[1999];
int dp[200000];
while(cin>>m>>n)
{
for(int i=0;i<n;i++)
{
scanf("%d%d",&num[i],&w[i]);
}
//memset(dp,0,sizeof(dp));
int mmax = 0;
for(int i=0;i<n;i++)
{
for(int j=0;j<num[i];j++)
{
for(int k=m;k>=w[i];k--)
{
dp[k] = max(dp[k],dp[k-w[i]]+w[i]);
mmax = max(mmax,dp[k]);
}
}
}
cout<<mmax<<endl;
}
return 0;
}
在完全背包的基础上更改一下就得到了正确代码
//多重背包
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<iostream>
#include<set>
#include<iomanip>
using namespace std;
int n,v;
int mmax;
int a[111111];
int dp[600100];
int c[600100];
const int inf = 0x3f3f3f3f;
int num[6010],w[6100];
int main()
{
while(cin>>v>>n)
{
for(int i =0;i<n;i++)
{
scanf("%d%d",&num[i],&w[i]);
}
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
{
memset(c,0,sizeof(c));
for(int j=w[i];j<=v;j++)
{
if(dp[j]<dp[j-w[i]]+w[i]&&c[j-w[i]]<num[i])
{
dp[j] = dp[j-w[i]]+w[i];
c[j] = c[j-w[i]]+1;
}
}
}
printf("%d\n",dp[v]);
}
return 0;
}
Xuhuang在挑战程序设计竞赛上改编了一道类似的题,于是得到了第三种算法:
改编自是否存在正好等于v的一道背包题
//多重背包
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<iostream>
#include<set>
#include<iomanip>
using namespace std;
int n,v;
int mmax;
int a[111111];
int dp[600100];
int c[600100];
const int inf = 0x3f3f3f3f;
int num[6010],w[6100];
int main()
{
while(cin>>v>>n)
{
for(int i =0;i<n;i++)
{
scanf("%d%d",&num[i],&w[i]);
}
memset(dp,-1,sizeof(dp));
dp[0] = 0;
for(int i=0;i<n;i++)
{
for(int j=0;j<=v;j++)
{
if(dp[j]>=0)dp[j] = num[i];
else if(j<w[i]||dp[j-w[i]]<=0)
{
dp[j] = -1;
}
else
dp[j] = dp[j-w[i]]-1;
}
}
int i;
for( i=v;i>=0;i--)
{
if(dp[i]>=0)break;
}
printf("%d\n",i);
}
return 0;
}
此外还有一种二进制算法,贴在这里供大家参考
http://blog.youkuaiyun.com/weinierzui/article/details/23671419