luogu3084 Photo 单调队列优化DP
题目大意
农夫约翰决定给站在一条线上的N(1 <= N <= 200,000)头奶牛制作一张全家福照片,N头奶牛编号1到N。于是约翰拍摄了M(1 <= M <= 100,000)张照片,每张照片都覆盖了连续一段奶牛:第i张照片中包含了编号a_i 到 b_i的奶牛。但是这些照片不一定把每一只奶牛都拍了进去。在拍完照片后,约翰发现了一个有趣的事情:每张照片中都有且仅有一只身上带有斑点的奶牛。约翰意识到他的牛群中有一些斑点奶牛,但他从来没有统计过它们的数量。 根据照片,请你帮约翰估算在他的牛群中最多可能有多少只斑点奶牛。如果无解,输出“-1”。
题解
状态的设计
我一开始看见了区间,便老是往数据结构那里想,花费了很长时间;后来想到了动规,希望用f(i, j)来表示前i头牛、前j张照片中最多会有多少斑点牛,却发现这样的决策具有后效性。
避免后效性的方法便是增加限制条件。我们让f(i)指第i头牛是有斑点的情况下前i头奶牛中最多会有多少斑点牛即可。
状态的转移
本题的难点之一在于可以转移的转移的状态范围不固定。i的前一头斑点牛必须在i所在任何区间的左侧,而且它要么被在i所在的左端点最靠左的区间a的相邻左侧的区间b内,要么位于a与b间不被区间包含的部分中。因此,可以转移到i的状态组成了一个区间[_cows[i].PrevL, _cows[i].PrevR]。PrevR便是a的左端点-1,PrevL便是b的左端点。因此递归式为f(i) = max(f[j]+1|j∈[_cows[i].PrevL, _cows[i].PrevR])。由于转移来源是个区间,所以我们可以用单调队列来优化。
PrevL, PrevR的求法
这是一个极其新颖的做法:若我们知道一个序列是单调不减的,有很多元素都重复,那么我们可以把序列中值开始变化的分界点的值求出来,然后从前到后或从后往前扫描一遍即可求出这个序列。
我们知道随着i的递增, _cows[i].PrevL,PrevR都是递增的。那么分界点在哪里呢?若PrevL变化,则i在一个区间的右端点+1处;若PrevR变化,则i在一个区间的右端点处。由于取min和取max的区别,我们PrevL从左到右扫描,PrevR从右到左扫描。
注意我们以上都没有提到“区间的左端点”这个位置,因为区间的左端点可能并不是PrevL的分界点,因为若该区间左侧有一段空白,PrevL不变。另外PrevR的分界点可能会漏掉,因为一段区间右端点以后不一定紧跟另一个区间的左端点。
最后输出的结果
错误求法是DP完后,对所有i取最大值。结果是会漏掉-1的情况。原因是F[i]指考虑负责包含[1, i]这些点的区间,以后的区间没有考虑。若最大值是在前面取的,一些右面的区间可就没有对应的点喽!
正确的做法是后部增加一个奶牛,求它的F值就对了。这样也可以迅速得知-1的情况。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int MAX_RANGE_CNT = 100010, MAX_ID = 200010, MINF = 0xcfcfcfcf;
int TotId, TotRange;
int F[MAX_ID]; struct Range
{
int L, R; bool operator < (const Range& a) const
{
return L < a.L;
}
}_ranges[MAX_RANGE_CNT]; struct Cow
{
int PrevL, PrevR;
}_cows[MAX_ID]; struct SlideWindow
{
private:
struct Queue
{
private:
int A[MAX_ID];
int Head, Tail; public:
Queue():Head(0), Tail(0){}
void push_back(int x) { A[Tail++] = x; }
void pop_back() { Tail--; }
int back() { return A[Tail - 1]; }
void pop_front() { Head++; }
int front() { return A[Head]; }
bool empty() { return Head == Tail; }
}IdQ; int Tail; public:
SlideWindow(): Tail(-1){} int Move(int tail, int len)
{
while (Tail < tail)
{
Tail++;
int head = Tail - len + 1;
while (!IdQ.empty() && IdQ.front() < head)
IdQ.pop_front();
while (!IdQ.empty() && F[IdQ.back()] < F[Tail])
IdQ.pop_back();
IdQ.push_back(Tail);
}
if (len <= 0)
return MINF;
return F[IdQ.front()];
} int GetMax()
{
return F[IdQ.front()];
}
}; void Read()
{
scanf("%d%d", &TotId, &TotRange);
for (int i = 1; i <= TotRange; i++)
scanf("%d%d", &_ranges[i].L, &_ranges[i].R);
TotId++;
} int NoAns; void SetCoverL()
{
sort(_ranges + 1, _ranges + TotRange + 1);
for (int i = 1; i <= TotId; i++)
_cows[i].PrevR = i - 1;
for (int i = 1; i <= TotRange; i++)
{
_cows[_ranges[i].R].PrevR = min(_cows[_ranges[i].R].PrevR, _ranges[i].L - 1);
_cows[_ranges[i].R + 1].PrevL = max(_cows[_ranges[i].R + 1].PrevL, _ranges[i].L);
}
for (int i = 2; i <= TotId; i++)
_cows[i].PrevL = max(_cows[i].PrevL, _cows[i - 1].PrevL);
for (int i = TotId - 1; i >= 1; i--)
_cows[i].PrevR = min(_cows[i].PrevR, _cows[i + 1].PrevR);
} int DP()
{
memset(F, MINF, sizeof(F));
F[0] = 0;
static SlideWindow g;
for (int i = 1; i <= TotId; i++)
{
F[i] = g.Move(_cows[i].PrevR, _cows[i].PrevR - _cows[i].PrevL + 1) + 1;
}
return F[TotId] < 0 ? -1 : F[TotId] - 1;
} int main()
{
Read();
SetCoverL();
printf("%d\n", DP());
return 0;
}
luogu3084 Photo 单调队列优化DP的更多相关文章
- BZOJ 3126 [USACO2013 Open]Photo (单调队列优化DP)
洛谷传送门 题目大意:给你一个长度为$n$的序列和$m$个区间,每个区间内有且仅有一个1,其它数必须是0,求整个序列中数字1最多的数量 神题,竟然是$DP$ 定义$f_{i}$表示第i位放一个1时,最 ...
- bzoj3126[Usaco2013 Open]Photo 单调队列优化dp
3126: [Usaco2013 Open]Photo Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 374 Solved: 188[Submit] ...
- bzoj 3126: [Usaco2013 Open]Photo——单调队列优化dp
Description 给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点 Input * Line 1: Two integers N and M. * Lines 2..M+ ...
- 单调队列优化DP,多重背包
单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...
- 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 ...
- hdu3401:单调队列优化dp
第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次 ...
- Parade(单调队列优化dp)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=2490 Parade Time Limit: 4000/2000 MS (Java/Others) ...
- BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP
BZOJ_3831_[Poi2014]Little Bird_单调队列优化DP Description 有一排n棵树,第i棵树的高度是Di. MHY要从第一棵树到第n棵树去找他的妹子玩. 如果MHY在 ...
- 【单调队列优化dp】 分组
[单调队列优化dp] 分组 >>>>题目 [题目] 给定一行n个非负整数,现在你可以选择其中若干个数,但不能有连续k个数被选择.你的任务是使得选出的数字的和最大 [输入格式] ...
随机推荐
- 9.Hierarchy Editor
Hierarchy Editor(层次编辑器) 用于定义3D图层的结构,向Ventuz渲染引擎发出“命令”,并指定命令的发生顺序.通常,每个层次节点都会导致对GPU的一个或多个调用,例如设置材质或渲染 ...
- python gdal 数组生成图片
cols = array.shape[1]rows = array.shape[0]originX = rasterOrigin[0]originY = rasterOrigin[1]driver = ...
- Glide4.0 centerCrop属性和圆角 冲突
首先致谢:https://blog.csdn.net/flyinbed_/article/details/75506062 咱们不是代码的生产者,只是代码的搬运工. 最近有个工作中有个需求就是展示的图 ...
- VMWare 安装Ubuntu 16.04
1.新建虚拟机 (1)点击文件-->新建虚拟机 (2)选择 自定义(高级)--> 下一步 (3)选择Workstation 12.0 --> 下一步 (4)选择 稍后安装操作系统 - ...
- 4星|《JAC写给外贸公司老板的企管书》:善总结爱学习、有业绩的老外贸的经验谈
作者从事外贸10余年,作出了业绩,也善总结.爱学习.爱分享.本书是作者在外贸行业的从业经验集.有一些战略方面的,比如开发小语种市场,大部分都是战术方面的操作细节(比如如何做营销),应该是非常适合从业者 ...
- MYSQL数据库迁移到ORACLE数据库
一.环境和需求1.环境 MySQL数据库服务器: OS version:Linux 5.3 for 64 bit mysql Server version: 5.0.45 Oracle数据库服务器: ...
- 【Centos7】Tomcat安装及一个服务器配置多个Tomcat
完成解压 参考 http://www.cnblogs.com/h--d/p/5074800.html https://www.cnblogs.com/tudou-22/p/9330875.html 步 ...
- 【Apache Kafka】一、Kafka简介及其基本原理
对于大数据,我们要考虑的问题有很多,首先海量数据如何收集(如Flume),然后对于收集到的数据如何存储(典型的分布式文件系统HDFS.分布式数据库HBase.NoSQL数据库Redis),其次存储 ...
- P4047 [JSOI2010]部落划分(最小生成树)
题目描述 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常发生争斗.只是,这一切都成为谜团了——聪 ...
- Vue.js:使用vue-cli快速构建项目
vue-cli是什么? vue-cli 是vue.js的脚手架,用于自动生成vue.js模板工程的. vue-cli怎么使用? 安装vue-cli之前,需要先安装了vue和webpack,不知道怎么安 ...