传送门:POJ1276
题意:给定一种新货币的多种面额和每种面额的数量和一个数额上限,问在不超过数额上限的前提下用给定的货币最多能凑出多大数额来。
思路:典型的多重背包,可以说是模板题,只不过本题中w[i]==v[i]而已。
可以用二进制优化,也可以单调队列优化。
不明白多重背包是啥的请自行搜索《背包九讲》。
二进制优化代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<vector>
#include<map>
#define ll long long
#define pi acos(-1)
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
int num[15],w[15],v[15];
int dp[100010];
int V,n;
void zero(int cost,int weight)
{
for(int i=V;i>=cost;i--)
dp[i]=max(dp[i],dp[i-cost]+weight);
}
void complet(int cost,int weight)
{
for(int i=cost;i<=V;i++)
dp[i]=max(dp[i],dp[i-cost]+weight);
}
void multi(int cost ,int amount ,int weight)
{
if(cost*amount>=V)
{
complet(cost,weight);
return;
}
int k=1;
while(k<amount)
{
zero(k*cost,k*weight);
amount-=k;
k=k*2;
}
zero(amount*cost,amount*weight);
}
//多重背包的二进制优化
int main()
{
while(~scanf("%d%d",&V,&n))
{
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
scanf("%d%d",&num[i],&w[i]),v[i]=w[i];
for(int i=1;i<=n;i++)
{
multi(w[i],num[i],v[i]);
}
printf("%d\n",dp[V]);
}
return 0;
}
单调队列优化(竟然还比上一种慢):
#include<stdio.h>
#include<string.h>
int w[15],c[15],v[15],dp[100010],V,n;
struct Queue
{
int num,value;
}que[250005];
int head,tail;
void enqueue (int x,int y)
{
while (head<=tail && que[tail].value<y) tail--;
que[++tail].num=x;que[tail].value=y;
}
void multipack()
{
int i,j,d;
memset(dp,0,sizeof(dp));
for (i=1 ; i<=n ; ++i)
{
if (c[i] > V/w[i]) c[i]=V/w[i];
for (d=0 ; d<w[i] ; ++d)
{
head=1;tail=0;
for (j=0 ; j<=(V-d)/w[i] ; ++j)
{
enqueue(j , dp[j*w[i]+d]-j*v[i]);
while (que[head].num<j-c[i] && head<=tail) head++;
dp[j*w[i]+d]=que[head].value+j*v[i];
}
}
}
}
int main()
{
while(~scanf("%d%d",&V,&n))
{
for(int i=1;i<=n;i++)
scanf("%d%d",&c[i],&w[i]),v[i]=w[i];
multipack();
printf("%d\n",dp[V]);
}
return 0;
}