【HDU2191】多重背包【DP多重背包】【单调队列优化】

题目描述

PS:鸡汤题干
在这里插入图片描述
更好的题目阅读体验

分析

这题裸的多重背包会TLE掉。下面就来介绍一下用单调队列来实现的时间复杂度为O(nm)的多重背包。
先来回顾裸的多重背包:

For (int i=1;i<=n;i++)
    For (int j=m;j>=0;j--)
       For (int k=1;k<=s[i]&&k*w[i]>=j;k++)
           F[j]=min(f[j],f[j-k*w[i]]+v[i]*k);

前面两个循环是去不掉的,想想如何去掉第三个循环,达到O(nm)的时间复杂度。

首先我们知道价格为X只能转换到X的倍数的状态,
所以我们**枚举余数(0~m-1)**表示一开始的状态
然后设一个y表示要把这个商品装几个
那么 f [ x ∗ w + k ] f[x∗w+k] f[xw+k]表示的是当前的最优价值,
又有 f [ x ∗ w + k ] − v ∗ x f[x∗w+k]−v∗x f[xw+k]vx表示未更新的状态
于是得到动态转移方程
f [ x ∗ w + k ] = m a x ( q [ h ] + v ∗ x ) f[x∗w+k]=max(q[h]+v∗x) f[xw+k]=max(q[h]+vx)
其他的话就是注意一些细节比如:初始化的数,数组每次清零。

上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int t,n,m,w,v,s; 
int q[1000001],f[1000001],num[1000001]; 
int main()
{
    cin>>t;
	for(int i=1;i<=t;i++)
	{
		cin>>n>>m;
		memset(f,0,sizeof(f));//一定要清零(因为多组数据) 
		for(int j=1;j<=m;j++)
		{
			cin>>w>>v>>s;//分别表示价格,重量(价值) ,数量 
			if(s>n/w) s=n/w;
			for(int k=0;k<=w-1;k++)//枚举余数!!!余数是0~w-1!! 
			{
				int h=1,t=0;//单调队列这种赋值
				for(int x=0;x<=(n-k)/w;x++)//21~34行单调队列维护 
				{
					int tmp=f[x*w+k]-v*x;
					while(h<=t&&q[t]<=tmp) 
					{
						t--;
					}
					t++;
					q[t]=tmp;
					num[t]=x;
					while(h<=t&&x-num[h]>s)
					{
						h++; 
					} 
					f[x*w+k]=max(f[x*w+k],q[h]+v*x);//状态转移方程 
				} 
			} 
		}
		cout<<f[n]<<endl;
	}    
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值