POJ1267 Cash machine 多重背包

传送门: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值