单调队列 hdu2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活

传送门:点击打开链接

题意:部分背包

思路:最简单的方法当然是二进制分解,不过最近在练习单调队列,用单调队列去做部分背包,也算是单调队列优化dp里迈出的第一步把~

使用范围:只有对于f1(x)=max/min(f2(k))+f3(x) 且f3(x)与k无关,k<x的这种dp,才能用单调队列去维护

对于部分背包这道题,本来的方程应该是dp[r+i*p]=max(dp[r+k*p]+(i-k)*h),k<i,0<=r<=p-1

那么只需要稍微变形一下,就能得到dp[r+i*p]=max(dp[r+k*p]-k*h)+i*h,所以就对应了我之前讲的那个式子了,f2(k)=dp[r+k*p]-k*h,f3(i)=i*h

然后这个维护最大值里面,还有一个限制条件,就是应该有i-k<=c,所以这道题就变成了求一个固定区间长度里的最大值的题目了,那么就能用单调队列来做了

有个地方要注意,就是每次要搞清楚队列什么时候清空,初始条件是什么,以及更新和维护的顺序

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;

const int MX = 1e3 + 5;

int dp[MX];
int cur, rear;

struct Node {
    int cost, id;
    Node() {}
    Node(int _id, int _c) {
        cost = _c; id = _id;
    }
} Q[MX];

int main() {
    int T, n, V; //FIN;
    scanf("%d", &T);
    while(T--) {
        memset(dp, 0, sizeof(dp));

        scanf("%d%d", &V, &n);
        for(int i = 1; i <= n; i++) {
            int p, h, c;
            scanf("%d%d%d", &p, &h, &c);

            for(int r = 0; r < p; r++) {
                cur = rear = 0;
                Q[rear++] = Node(0, dp[r]);
                for(int k = 1; k * p + r <= V; k++) {
                    while(cur < rear && k - Q[cur].id > c) cur++;
                    int now = k * p + r, top = Q[cur].cost;

                    Node temp(k , dp[now] - k * h);
                    while(cur < rear && Q[rear - 1].cost < temp.cost) rear--;
                    Q[rear++] = temp;

                    dp[now] = max(dp[now], top + k * h);
                }
            }
        }
        printf("%d\n", dp[V]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值