zoukankan      html  css  js  c++  java
  • 【单调队列】【P1776】宝物筛选

    传送门

    Description

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

    Input

    第一行为一个整数(N)(w),分别表示宝物种数和采集车的最大载重。
    接下来(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~leq~n~leq~100)
    (1~leq~w~leq~40000)
    数据保证合法

    Solution

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

    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,'
    ',true);
    	return 0;
    }
    

    Summary

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

  • 相关阅读:
    一个比较好用的网络库
    Live Writer Beta 测试
    WinForm中如何设置MDI父窗体的背景图片
    使文件下载的自定义连接支持 FlashGet 的断点续传多线程链接下载! C#/ASP.Net 实现! 转
    关于从剪贴板获得截图
    我的作品图书馆信息管理系统
    很久以前用VB写的小游戏
    Web2.0时代,RSS你会用了吗?_CSDN
    VFP下利用API调用帮助
    VFP中轻松绑定 Windows 事件
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9603318.html
Copyright © 2011-2022 走看看