[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. centos启动提示unexpected inconsistency RUN fsck MANUALLY

    今天一台虚拟机背后的物理机故障了,主机迁移后变成了 read only filesystem.上面部署了很多长连接服务,没有关掉就直接reboot,报错: unexpected inconsisten ...

  2. 在一个div上增加遮罩

    有一个需求是给一个视频增加遮罩 我研究了下 抽象出来就是给一个div增加遮罩 原理是:最外层的div使用relative定位     然后里面放两个子div  一个是不被遮的  另一个是遮罩(用abs ...

  3. c++11 指针空值

    1. 引入nullptr的必要性: 典型的指针初始化是将其指向一个空的位置.比如: int* my_ptr = 0; int* my_ptr = NULL; 一般情况下,NULL是一个宏定义. #un ...

  4. Python3解leetcode First Bad Version

    问题描述: You are a product manager and currently leading a team to develop a new product. Unfortunately ...

  5. 【2019 Multi-University Training Contest 10】

    01: 02: 03:https://www.cnblogs.com/myx12345/p/11671692.html 04: 05:https://www.cnblogs.com/myx12345/ ...

  6. js 和jquery

    1. js 全称 javascript 是 web客户端 运行的 解释性语言.. 2. jquery 只不过是 js 封装 简化了 ajax 和 dhtml 的 一款js 框架而已. 简单来说 Jqu ...

  7. laravel的使用

    1.先下载composer.phar 下载地址:https://getcomposer.org/download/ 把composer.phar拷贝到自己的项目目录中,执行以下代码: php comp ...

  8. selenium设定请求头

    如果只是需要设定User-Agent可以用如下所示方式 dcap = dict(DesiredCapabilities.PHANTOMJS)dcap["phantomjs.page.sett ...

  9. RSA - 原理、特点(加解密及签名验签)及公钥和私钥的生成

    Wiki - RSA加密演算法 Wiki - 欧拉函数 Wiki - 模反元素 ASN.1 格式标准 RSA算法原理(二) 注意: RSA 加密或签名后的结果是不可读的二进制,使用时经常会转为 BAS ...

  10. js面向对象程序设计之构造函数

    再上一篇的开头说了创建对象的两种方式,一种是Object构造函数的方式,一种是对象字面量的方法.但这些方式创建多个对象的时候都会产生大量的重复代码.经过技术的进步也演化出来许多的创建对象的模式.本章会 ...