【单调队列】【P1776】宝物筛选

传送门

Description

终于,破解了千年的难题。小FF找到了王室的宝物室,里面堆满了无数价值连城的宝物……这下小FF可发财了,嘎嘎。但是这里的宝物实在是太多了,小FF的采集车似乎装不下那么多宝物。看来小FF只能含泪舍弃其中的一部分宝物了……小FF对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小FF有一个最大载重为W的采集车,洞穴里总共有n种宝物,每种宝物的价值为vi,重量为wi,每种宝物有mi件。小FF希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

Input

第一行为一个整数Nw,分别表示宝物种数和采集车的最大载重。
接下来n行每行三个整数,其中第i行第一个数表示第i类品价值,第二个整数表示一件该类物品的重量,第三个整数为该类物品数量。

Output

输出仅一个整数ans,表示在采集车不超载的情况下收集的宝物的最大价值。

Sample Input

4 20
3 9 3
5 9 1
9 4 2
8 1 3

Sample Output

47

Hint

1  n  100
1  w  40000
数据保证合法

Solution

多重背包问题。设fi,j是前i个物品重量是j的最大价值。考虑转移。朴素方法在转移时枚举该物品使用多少个。由于物品个数与w同阶,所以时间复杂度是O(nm2),其中m代表最大载重量。
考虑优化。
考虑对第i个物品,重量为j时只能从j  k × wi转移。其中w代表重量。由于是连续转移,所以被转移的状态一定是单调不降的。考虑使用单调队列优化。
按照j Mod wi分类,同一类之间才能转移,维护一群单调队列。每次判断出队时把队首元素加上在这个位置应该加的价值再与当前元素比较。

while((front <= end) && ((frog[pos][que[end]]+(k-que[end])/b*a)) <= frog[pos][k]) --end;

这里k是当前枚举到的背包重量。a是价值,b是该物品的重量。
同时,也可以不选择该物品,所以枚举所有重量与从上一行继承进行转移。

for(rg int j=1;j<=w;++j) frog[cur][j]=mmax(frog[cur][j],frog[pos][j]);

代码如下

Code

#include<cstdio>
#define rg register
#define ci const int
#define cl const long long int

typedef long long int ll;

namespace IO {
	char buf[90];
}

template<typename T>
inline void qr(T &x) {
	char ch=getchar(),lst=' ';
	while(ch>'9'||ch<'0') lst=ch,ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(lst=='-') x=-x;
}

template<typename T>
inline void write(T x,const char aft,const bool pt) {
	if(x<0) x=-x,putchar('-');
	int top=0;
	do {
		IO::buf[++top]=x%10+'0';
		x/=10;
	} while(x);
	while(top) putchar(IO::buf[top--]);
	if(pt) putchar(aft);
}

template<typename T>
inline T mmax(const T a,const T b) {if(a>b) return a;return b;}
template<typename T>
inline T mmin(const T a,const T b) {if(a<b) return a;return b;}
template<typename T>
inline T mabs(const T a) {if(a<0) return -a;return a;}

template<typename T>
inline void mswap(T &a,T &b) {
	T temp=a;a=b;b=temp;
}

const int maxn = 110;
const int maxm = 500010;

int n,w,cur,pos=1,front,end,a,b,c,ans;
int frog[2][maxm],que[maxm];


int main() {
	qr(n);qr(w);
	for(rg int i=1;i<=n;++i) {
		a=b=c=0;qr(a);qr(b);qr(c);
		rg int upceil=b*c;
		for(rg int j=0;j<b;++j) {
			que[front=end=1]=j;
			for(rg int k=j+b;k<=w;k+=b) {
				while((front <= end) && ((k-que[front]) > upceil)) ++front;
				while((front <= end) && ((frog[pos][que[end]]+(k-que[end])/b*a)) <= frog[pos][k]) --end;
				que[++end]=k;
				frog[cur][k]=frog[pos][que[front]]+(k-que[front])/b*a;
			}
		}
		for(rg int j=1;j<=w;++j) frog[cur][j]=mmax(frog[cur][j],frog[pos][j]);
		mswap(cur,pos);
	}
	for(rg int i=1;i<=w;++i) ans=mmax(ans,frog[pos][i]);
	write(ans,'\n',true);
	return 0;
}

Summary

在使用手写队列时,初始化que[front=end=1]=0。判断队列不为空的条件是front  end

posted @   一扶苏一  阅读(363)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示