[poj1821]Fence

有 N 块木板从左至右排成一行,有 M 个工匠对这些木板进行粉刷,每块木板至多被粉刷一次。第 i 个工匠要么不粉刷,要么粉刷包含木板 Si 的,长度不超过Li 的连续一段木板,每粉刷一块木板可以得到 Pi 的报酬。求如何安排能使工匠们获得的总报酬最多。

1<=N<=16000,1<=M<=100

输入

NK

L1 P1 S1

L2 P2 S2

...

LK PK SK

输出

输出包含一个整数,即最大总收入。

样例输入:

8 4

3 2 2

3 2 3

3 3 5

1 1 7

样例输出:

17

先把所有工匠按照\(Si\)排序,这样一来,每个工匠粉刷的木板一定在上一个工匠之后,使我们能够按顺序进行线性 DP。

设\(F[i,j]\)表示安排前\(i\)个工匠粉刷前\(j\)块木板(可以有空着不刷的木板),工匠能获得的最多报酬。

1.第\(i\)个工匠可以什么也不刷,此时\(F[i,j]=F[i-1,j]\)。

2.第\(j\)块木板可以空着不刷,此时\(F[i,j]=F[i,j-1]\)。

3.第\(i\)个工匠粉刷第\(k+1\)块到第\(j\)块木板。根据题意,该工匠粉刷总数不能超过\(Si\),所以需要满足: \(k+1<=Si<=j\)并且\(j-k<=Li\)。于是,有状态转移方程:

\[F[i,j]=max(F[i-1,k]+Pi*(j-k)) (j-Li<=k<=Si-1,j>=Si)
\]

我们重点来看这个方程怎么优化。首先,在考虑内层循环\(j\)以及决策\(k\)时,可把外层循环变量\(i\)看作定值。这样一来,状态转移方程中的各项可分为两部分:

1.\(Pi*j\),除定值\(i\)外,只有状态变量\(j\)。

2.\(F[i-1,k]-Pi*k\),除定值\(i\)外,只有决策变量\(k\)。状态转移方程可写为:

\[F[i,j]=Pi*j+max(F[i-1,k]-Pi*k)
\]

当 \(j\) 增大时,\(k\) 的取值范围上界 \(Si-1\) 不变,下界 \(j-Li\) 变大。这时

我们来比较任意两个决策 \(k1\) 和 \(k2\)。不妨设 \(k1<k2<=Si-1\)。因为 \(k2\) 比 \(k1\) 更靠后,所以随着 \(j\) 的增加,\(k1\) 会比 \(k2\) 更早从范围\([j-Li,Si-1]\)中排除。如果还满足\(F[i-1,k1]-Pi*k1<=F[i-1,k2]-Pi*k2\)那么就意味着 k2 不但比 k1 优,还比 k1 的存活时间更长。在这种情况下,k1 就是一个无用的决策,应该被排除出候选集合。综上所述,我们可以维护一个决策点 k 的单调递增。数值\(F[i-1,k]-Pi*k\) 单调递减的队列。只有这个队列中的决策才有可能在某一时刻成为最优决策。这个单调队列支持如下操作:

1.当 j 变大时,检查队头元素,把小于 j-Li 的决策出队。

2.需要查询最优决策时,队头即为所求。

3.有一个新的决策需要加入队列时,在队尾检查 F[i-1,k]-Pi*k 的单调性,把无用决策从队尾直接出队,最后把新决策加入队列。

在本题具体来说,当内循环开始时(j==Si),建立一个空的单调队列,把\(max(Si-Li,0),Si-1\)中的决策依次加入候选集合(执行操作 3)。对于每个 \(j=Si~k\),先在队头检查决策合法性(操作 1),然后取队头为最优决策(操作 2)进行状态转移。因为每个决策至多入队,出队一次,故转移的时间复杂队均弹 \(O(1)\)。整个算法的时间复杂度为 \(O(NM)\)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int read()
{
int x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*w;
}
int dp[110][16010];
int team[16010];
struct node{
int l,p,s;
}f[110];
int cmp(node p,node q)
{
return p.s<q.s;
}
int main()
{
int n=read(),k=read();
for(int i=1;i<=k;i++)
{
f[i].l=read();f[i].p=read();f[i].s=read();
}
sort(f+1,f+1+k,cmp);
for(int i=1;i<=k;i++)
{
int l=1,r=0;
for(int p=max(0,f[i].s-f[i].l);p<f[i].s;p++)
{
while(l<=r&&dp[i-1][team[r]]-f[i].p*team[r]<=dp[i-1][p]-f[i].p*p)
r--;
team[++r]=p;
}
for(int j=1;j<=n;j++)
{
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
if(j>=f[i].s)
{
while(l<=r&&team[l]<j-f[i].l) l++;
if(l<=r)
dp[i][j]=max(dp[i][j],dp[i-1][team[l]]+f[i].p*(j-team[l]));
}
}
}
cout<<dp[k][n];
}

[POJ1821]Fence(单调队列优化dp)的更多相关文章

  1. $Poj1821\ Fence\ $单调队列优化$DP$

    Poj   Acwing Description 有N块木板等待被M个工匠粉刷,每块木板至多被刷一次.第i个工匠要么不粉刷,要么粉刷包含木块Si的,长度不超过Li的连续的一段木板,每粉刷一块可以得到P ...

  2. poj1821 Fence(单调队列优化dp)

    地址 一排N个木板,M个工匠站在不同位置$S_i$,每个人可以粉刷覆盖他位置的.最长长度为$L_i$木板段,每刷一个有$P_i$报酬.同一木板只刷一次.求最大报酬. 根据每个人的位置dp,设$f[i] ...

  3. POJ 1821 Fence(单调队列优化DP)

    题解 以前做过很多单调队列优化DP的题. 这个题有一点不同是对于有的状态可以转移,有的状态不能转移. 然后一堆边界和注意点.导致写起来就很难受. 然后状态也比较难定义. dp[i][j]代表前i个人涂 ...

  4. poj 1821 Fence 单调队列优化dp

    /* poj 1821 n*n*m 暴力*/ #include<iostream> #include<cstdio> #include<cstring> #incl ...

  5. 单调队列优化DP——习题收集

    前言 感觉可以用单调队列优化dp的模型还是挺活的,开个随笔记录一些遇到的比较有代表性的模型,断续更新.主要做一个收集整理总结工作. 记录 0x01 POJ - 1821 Fence,比较适合入门的题, ...

  6. 单调队列优化DP,多重背包

    单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...

  7. bzoj1855: [Scoi2010]股票交易--单调队列优化DP

    单调队列优化DP的模板题 不难列出DP方程: 对于买入的情况 由于dp[i][j]=max{dp[i-w-1][k]+k*Ap[i]-j*Ap[i]} AP[i]*j是固定的,在队列中维护dp[i-w ...

  8. hdu3401:单调队列优化dp

    第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次 ...

  9. Parade(单调队列优化dp)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2490 Parade Time Limit: 4000/2000 MS (Java/Others)    ...

  10. BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP

    BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP Description 有一排n棵树,第i棵树的高度是Di. MHY要从第一棵树到第n棵树去找他的妹子玩. 如果MHY在 ...

随机推荐

  1. selenium 自动化的坑(3)

    一天一坑系列(3) 今天不讲我是怎么定位了吧,今天讲的是关于弹窗的. 基于业务,一键全否之后需要二次确认,会弹出提示框,你会不会认为这是alert弹框?经过仔细查看元素,确认不是弹框,明明是div嘛, ...

  2. 使用getchar和putchar输入输出单个字符

    getchar()和putchar()只能用于输入输出单个字符,而不能字符串. #include<iostream> using namespace std; int main(){ ch ...

  3. php range()函数 语法

    php range()函数 语法 作用:创建一个包含指定范围的元素的数组.dd马达哪家好 语法:range(low,high,step) 参数: 参数 描述 low  必需.规定数组的最低值. hig ...

  4. iOS7上leftBarButtonItem无法实现滑动返回的完美解决方案

    今天遇到了在iOS7上使用leftBarButtonItem却无法响应滑动返回事件的问题,一番谷歌,最后终于解决了,在这里把解决方案分享给大家. 在iOS7之前的系统,如果要自定义返回按钮,直接设置b ...

  5. windown 使用python 自动切换网络

    由于工作环境的特殊性,有时需要切换到内网工作,有时需要通过手机连接外网,想自动运行一个脚本就做完这事 首先在PC机上安装一个无线网卡,例如360wifi就可以. 除了python,还用到了DOS命令, ...

  6. 1204C Anna, Svyatoslav and Maps

    题目大意 给你一个有向图和一个路径 让你在给定路径中选出尽量少的点使得新路径的最短路长度和原路径相等 给定路径相邻两点间距离为1 分析 先floyd求出两点间最短路 之后每次对于点i找到所有跟它的最短 ...

  7. JPEG 存储分割后的图象会产生锯齿

    分割后的图象如果保存为 JPG 格式,物体边界会产生锯齿状的很暗的像素. 如下图所示. 这会给后续处理带了噪声, 因此,保存分割后的图象最好用无损压缩格式,如 bmp, png 等.

  8. scrapy过滤重复数据和增量爬取

    原文链接 前言 这篇笔记基于上上篇笔记的---<scrapy电影天堂实战(二)创建爬虫项目>,而这篇又涉及redis,所以又先熟悉了下redis,记录了下<redis基础笔记> ...

  9. pandas 增删改查

    原文链接:https://blog.csdn.net/zhangchuang601/article/details/79583551 准备工作:增.删.改.查的方法有很多很多种,这里只展示出常用的几种 ...

  10. v-if指令

    <!doctype html> <html> <head> <meta charset="UTF-8"> <title> ...