标签(空格分隔): dp 单调队列优化


题目描述

有N块木板从左到右排成一行,有M个工匠对这些木板进行粉刷,每块木板至多被粉刷一次。

第 i 个木匠要么不粉刷,要么粉刷包含木板 \(S_i\) 的,长度不超过 $ L_i $ 的连续的一段木板,每粉刷一块可以得到 $ P_i $ 的报酬。

不同工匠的\(S_i\)不同。

请问如何安排能使工匠们获得的总报酬最多。

输入格式

第一行包含两个整数N和M。

接下来M行,每行包含三个整数Li,Pi,Si。

输出格式

输出一个整数,表示结果。

数据范围

1≤N≤16000,

1≤M≤100,

1≤Pi≤10000

输入样例:

8 4

3 2 2

3 2 3

3 3 5

1 1 7

输出样例:

17


显然,这是一道单调队列优化的模板题。

首先,我们考虑这个单调队列。

什么是单调队列 ?

单调队列, 指的是一个保存当前状态的决策点集合的队列。 队列的队首即是当前的最优决策。但在状态转移的过程中,需要不断维护。时间复杂度为O(阶段数 * 状态数)

维护单调队列有两种方式。

  1. 检查队首的合法性
  2. 在队尾删除无用决策

提一嘴,这里的单调是k单调递增,calc(i,k)单调递减


首先, 这一题有三个原始状态状态:

  1. 第i个painter什么也不刷, 即f[i - 1,j]
  2. 第j块板子不刷,即f[i, j - 1]
  3. 第i个工匠粉刷 k + 1 到 j 块板子 。

    由题面得: 该工匠粉刷不超过Li块,且必须刷Si,所以需要满足 :
    \[k + 1 <= S_i <= i \qquad and \qquad j - k <= L_i
    \]

所以有状态转移方程 :

$$ f[i, j] = \max_{ j - L_i \leqslant k \leqslant S_i - 1} { f[i - 1, k] + P_i * (j - k) } $$ 显然,在决策的过程中 \(P_i * j\) 是一个“等效”的常量, 所以将原转移方程改写为 :

$$ f[i, j] = P_i * j + \max_{ j - L_i \leqslant k \leqslant S_i - 1} { f[i - 1, k] - P_i * k } $$

其中, 我们就可以把 \(f[i - 1, k] - P_i * k\) 写成下文的calc函数

剩下的和着代码一起讲。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 16000 + 5;
const int maxm = 100 + 5;
int n, m;
int Queue[maxn];// 单调队列本体
struct node {
int l, p, s;
}painter[maxm];
//代表每个粉刷匠的l,p,s
int f[maxm][maxn];
//f[i][j] : 代表考虑第i位粉刷匠刷到第j块板子(中间可以有不刷的)能get的最大报酬
int calc(int i, int k){
return f[i - 1][k] - painter[i].p * k;
}
bool cmp(node a, node b){
return a.s < b.s;
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1;i <= m;i ++){
scanf("%d%d%d", &painter[i].l, &painter[i].p, &painter[i].s);
}
sort(painter + 1, painter + 1 + m, cmp);
//排序,使得每个粉刷匠刷的木板都在前一个之后,我们就可以按顺序进行dp
for(int i = 1;i <= m;i ++){
int h = 1, t = 0;//head & tail
for(int k = max(0, painter[i].s - painter[i].l);k <= painter[i].s - 1;k ++){
while(h <= t && calc(i, Queue[t]) <= calc(i, k))t --;
//当新决策比你小又比你强,那你就打不过他了QAQ。 人话:当新决策点的报酬比当前队尾的大,又因为在j不断增加的过程中当前队尾的决策点一定比新决策点更早从[j - Li, Si - 1]退出,因此新决策点的“生存能力”较强,则队尾一定为无用决策,delete掉,最后加入新的决策点
Queue[++ t] = k;
}//这几行是用来计算初始决策的。把从max(Si - Li, 0) ~ s[i] - 1的优秀的决策点取出,检查,入队
for(int j = 1;j <= n;j ++){
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
//不刷时的转移
if(j >= painter[i].s){
while(h <= t && Queue[h] < j - painter[i].l){
//当队首不在[j - Li, Si - 1]之中时,无法刷到Si,所以该退役了。
h ++;
}
if(h <= t){
f[i][j] = max(f[i][j], calc(i, Queue[h]) + painter[i].p * j);
//若队中还有决策点,则最优的决策点就为队首。
}
}
}
}
cout << f[m][n];
//输出了
return 0;
}

AcWing 298. 围栏 (POJ1821)的更多相关文章

  1. AcWing 329. 围栏障碍训练场

    大型补档计划 题目链接 考虑模拟这个过程. \(f[i][0 / 1]\) 表示从第 \(i\) 个围栏的 左/右端点开始往下走,走到原点的最小花费. 转移很容易想到,就是考虑找到一个往下走第一个碰到 ...

  2. Acwing P298 围栏

    Analysis ①首先将所有粉刷匠,按照必须刷的小木块Si从小到大排序. 上面这个操作为了保证我们可以顺序处理. ②我们可以设f[i][j]表示为,前i个粉刷匠,刷了前i个木块.可以有些木块选择不刷 ...

  3. csp-s 考前刷题记录

    洛谷 P2615 神奇的幻方 洛谷 P2678 跳石头 洛谷 P1226 [模板]快速幂||取余运算 洛谷 P2661 信息传递 LOJ P10147 石子合并 LOJ P10148 能量项链 LOJ ...

  4. 2021record

    2021-10-14 P2577 [ZJOI2004]午餐 2021-10-13 CF815C Karen and Supermarket(小小紫题,可笑可笑) P6748 『MdOI R3』Fall ...

  5. Acwing:102. 最佳牛围栏(前缀和 + 二分)

    农夫约翰的农场由 NN 块田地组成,每块地里都有一定数量的牛,其数量不会少于1头,也不会超过2000头. 约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最 ...

  6. AcWing 102. 最佳牛围栏

    农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于1头,也不会超过2000头. 约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大 ...

  7. AcWing 309. 装饰围栏

    题目链接 这道题与下一章的数位\(dp\)解题思路十分一致. 把寻找答案变成按位(并且是字典序从小到大)枚举当前这一位可以填的情况. 通过\(dp\)预处理的信息告诉我们可行性,就可以把答案紧逼到一个 ...

  8. Acwing-102-最佳牛围栏(二分,实数)

    链接: https://www.acwing.com/problem/content/104/ 题意: 农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于1头,也不会超过200 ...

  9. 地理围栏算法解析(Geo-fencing)

    地理围栏算法解析 http://www.cnblogs.com/LBSer/p/4471742.html 地理围栏(Geo-fencing)是LBS的一种应用,就是用一个虚拟的栅栏围出一个虚拟地理边界 ...

随机推荐

  1. 老板,来五道misc

    开个杂项坑 穿越时空的思念 音频隐写,audacity分离音道,摩斯密码一把锁 金三胖 是个gif,明显能感觉到里面藏有flag stegsolve逐帧分离太low了,直接用脚本一把梭 import ...

  2. Interface 接口详解

    简介 接口主要用来描述类具有哪些功能,并不给出每个功能的具体实现方式.一个类可以实现一个或多个接口,并在需要接口的地方,随时使用实现了响应接口的对象. 在 Java 程序设计语言中,接口不是类,而是对 ...

  3. java线程池拒绝策略使用实践

    前言 线程池是开发过程中使用频率较高的一个并发组件之一,本篇会结合踩刀哥之前的实践经验来分享一下线程池拒绝策略的真实使用场景,至于线程池内部原理只会简单介绍,有需要的可以自行上网学习. 线程池工作机制 ...

  4. Flask 中的MTV架构之Views

    Flask 中的MTV架构之Views 1.MVC与MTV 1.1 MVC ​ M:model,模型,数据模型 ​ V:view,视图,负责数据展示 ​ C:controller,控制器,负责业务逻辑 ...

  5. js音乐播放器【简洁】

    辞职的第二天没有去找工作还,准备回家. 但到了火车站才发现沃特玛的买的票不是在这个火车站坐. 这就耽误了行程...... 说出来真舒服!!!淦 代码 这里已经上传到码云了,大家可以直接引用. 目前只有 ...

  6. SpringCloud之Gateway

    一.为什么选择SpringCloud Gateway而不是Zuul? Gateway和Zuul的职责一样,都承担着请求分发,类似Nginx分发到后端服务器. 1.SpingCloud Gateway ...

  7. vim实现CTRL+S为保存快捷键

    用vim正撸代码撸的飞起,突然Xshell就掉线了,真是太蛋疼了. 于是开始怀念起vs下撸代码时随时随地ctrl+s保存的快捷键,百度了一下,网上的vim实现ctrl+s保存的快捷键设置都有问题,自己 ...

  8. css 两边宽度固定中间自适应宽度

    #content_left{   //左 width:73px; height:100px; vertical-align:middle; margin-right:-100px; float:lef ...

  9. C# 8: 可变结构体中的只读实例成员

    在之前的文章中我们介绍了 C# 中的 只读结构体(readonly struct)[1] 和与其紧密相关的 in 参数[2]. 今天我们来讨论一下从 C# 8 开始引入的一个特性:可变结构体中的只读实 ...

  10. 腾讯云--对象存储cos绑定自定义域名

    1.登录腾讯云控制台,找到对象存储一栏 2.选择一个你想绑定域名的存储桶 3.进入你选择的存储桶,点击域名管理 4.选择自定义源站域名.在域名处填写你要设置的自定义域名,在源站类型处选择静态网站源站, ...