一、Watching Fireworks is Fun(紫题)

题目:一个城镇有n个区域,从左到右1编号为n,每个区域之间距离1个单位距离节日中有m个烟火要放,给定放的地点ai,时间ti当时你在x,那么你可以获得∣bi-ai-x∣的幸福值,你每个单位时间可以移动不超过d个单位距离。你的初始位置是任意的(初始时刻为1),求你通过移动能获取到的最大的开心值。

题目大意:有n个点,在这之中有m个点有烟花,分别会在t时刻燃放,每个烟花的幸福值是b。每一个单位时间内人可以走d个单位距离,起始点任意,在x位置观看a点的烟花可得到(a-abs(b-x))的幸福值,问最大幸福值是啥。

………………………………是不是有点看蒙了,不要慌,我们再借助样例来分析一下。

输入格式:第一行三个数n,m,d(nmd就离谱),n,d<=150000,m<=300。

   接下来m行包括三个整数ai,bi,ti,ai<=150000,bi,ti<1e9,(保证ti>ti-1)。第i行表示第i个烟花。

输出格式:输出一个整数 —— 从观看所有烟花获得的最大幸福总和。

样例1:

输入:

50 3 1----------->一共50个点,有3个烟花,每一秒只能走1格

49 1 1----------->第一个烟花在49位置,贴脸看幸福值为1,在第1秒燃放

26 1 4----------->第二个烟花在26位置,贴脸看幸福值为1,在第4秒燃放

6 1 10----------->第三个烟花在6位置,贴脸看幸福值为1,在第10秒燃放

输出:

-31(看到烟花了还不开心就离谱)

一种可能的方案:

第1秒在29位置(幸福值(1-(49-29)=-19)),第4秒到了26位置(幸福值(1-(26-26)=1)(1-19=-18)),第10秒到了20位置(幸福值(1-(20-6)=-13)(-18-13=-31))

。。。分析完一个样例之后,是不是对题目大概清晰了些呢,那么我们可以开始讨论思路了

思路:

1.线性dp:f[i][j]表示第i个烟花燃放时处在j的最大幸福值,转移方程显然是f[i][j]=f[i-1][j-d*(t[i]-t[i-1])~j+d*(t[i]-t[i-1]));简单来说就是从可能转移到现在烟花的位置转移过来。

2.根据上面的思路,我们的时间效率是m(枚举i)×n(枚举j)×n(枚举d)显然要超时,所以需要优化。

3.我们注意到,在j从小到大枚举过程中,(H=d×(t[i]-t[i-1]))j-H~j+H的区间大小几乎是不变的,就像一个“窗口”从1到n扫过一样,这显然是单调队列的模型,所以我们可以使用其优化,可以把枚举d的那一层复杂度变成常数。

具体操作:

1.每遍历一个新的j的时候,我们枚举k从j到j+H(j-H当作限制用来出队),显然,k并不需要随着每一个j而从头遍历,只需记录一定区间(j-H~j+H)内的最大值即可,那么我们可以边读边扫,直到扫完整个区间1~n为止。

2.利用单调队列的特性来处理区间最大值,在每进队一个新的k时候,比较一下它是否比队尾更优,如果如此,那么把队尾弹出,再把k入队即可(因为在“窗口”向右扫的过程中,靠右的点比它左边的点更晚失效(指<j-H),如果比队尾更优,那队尾这个点就没有存在的必要了),同时再遍历j的时候,处理一下队首元素是否已经“失效”,如果如此,也弹出。

3.也许你注意到了,在刚才的过程中出现了“插入队尾”,“弹出队尾”,“弹出队首”三种操作,对于这样的操作,显然双端队列很适合处理。

4.那么问题来了如何比较“更优”呢,我们知道,第i行的状态由i-1行转移过来,那么只要i-1行的第k个状态所对应的幸福值更大,他就优呗(奇怪的废话增加了)

5.转移方程dp[i][j]=dp[i-1][q.front()];(根据单调队列的特性,队首元素是此区间内最大值)

好了,思路弄懂了,就直接上代码。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=150000+10;
LL f[500][maxn];
int main(){
int n,m,d,tt=0;
scanf("%d%d%d",&n,&m,&d);
for(int i=1;i<=m;i++){
int a,b,t;
scanf("%d%d%d",&a,&b,&t);
LL H=1LL*d*(t-tt);
tt=t;//这里并没有用t数组来保存两个烟花之间相差的时间,但这样操作也可以
deque<int> q;
int k=1;//k不随j的改变而重新遍历,所以在j循环之前定义
for(int j=1;j<=n;j++){
for(;k<=n&&k<=j+H;k++){//k的范围
while(!q.empty()&&f[i-1][k]>=f[i-1][q.back()])q.pop_back();
//如果k比队尾更优,队尾出队
q.push_back(k);//k入队
}
while(!q.empty()&&q.front()<j-H){
q.pop_front();
}
//每次遍历一个j,队首就可能失效一次
f[i][j]=f[i-1][q.front()]+b-abs(a-j);
//记录i,j情况下最大幸福值
}
}
LL Max=-0x3f3f3f3f3f3f3f3f;
for(int i=1;i<=n;i++){
Max=max(Max,f[m][i]);
}
//找所有看完烟花的状态中(f[m][~])最大的那个,也就是最后站的地方
printf("%lld",Max);
return 0;
}

这里使用了#include<queue>中的双端队列,显然效率上要差一些,建议数组模拟

下面附上数组模拟代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
const int maxn=150000+10;
LL f[500][maxn];
int main(){
int n,m,d,tt=0;
scanf("%d%d%d",&n,&m,&d);
for(int i=1;i<=m;i++){
int a,b,t;
scanf("%d%d%d",&a,&b,&t);
LL H=1LL*d*(t-tt);
tt=t;
LL qq[maxn],head=1,tail=0;
int k=1;
for(int j=1;j<=n;j++){
for(;k<=n&&k<=j+H;k++){
while(head<=tail&&f[i-1][k]>=f[i-1][qq[tail]])tail--;
qq[++tail]=k;
}
while(head<=tail&&qq[head]<j-H)head++;
f[i][j]=f[i-1][qq[head]]+b-abs(a-j);
}
}
LL Max=-0x3f3f3f3f3f3f3f3f;
for(int i=1;i<=n;i++){
Max=max(Max,f[m][i]);
}
printf("%lld",Max);
return 0;
}

这里还有一个小问题:在队首出栈时我们的while循环写在了k的循环的外面,这是为什么呢,会有影响吗。

会有影响的!!!

给你们一个错误样例,可以思考一下为什么(调试一下就知道了)

100 2 5
29 78 53
78 13 60

单调队列+线性dp题Watching Fireworks is Fun (CF372C)的更多相关文章

  1. 【简洁易懂】CF372C Watching Fireworks is Fun dp + 单调队列优化 dp优化 ACM codeforces

    题目大意 一条街道有$n$个区域. 从左到右编号为$1$到$n$. 相邻区域之间的距离为$1$. 在节日期间,有$m$次烟花要燃放. 第$i$次烟花燃放区域为$a_i$ ,幸福属性为$b_i$,时间为 ...

  2. 单调队列以及单调队列优化DP

    单调队列定义: 其实单调队列就是一种队列内的元素有单调性的队列,因为其单调性所以经常会被用来维护区间最值或者降低DP的维数已达到降维来减少空间及时间的目的. 单调队列的一般应用: 1.维护区间最值 2 ...

  3. 【专题系列】单调队列优化DP

    Tip:还有很多更有深度的题目,这里不再给出,只给了几道基本的题目(本来想继续更的,但是现在做的题目不是这一块内容,以后有空可能会继续补上) 单调队列——看起来就是很高级的玩意儿,显然是个队列,而且其 ...

  4. 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 ...

  5. 【HDU 3401 Trade】 单调队列优化dp

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3401 题目大意:现在要你去炒股,给你每天的开盘价值,每股买入价值为ap,卖出价值为bp,每天最多买as ...

  6. [小明打联盟][斜率/单调队列 优化dp][背包]

    链接:https://ac.nowcoder.com/acm/problem/14553来源:牛客网 题目描述 小明很喜欢打游戏,现在已知一个新英雄即将推出,他同样拥有四个技能,其中三个小技能的释放时 ...

  7. 2018.09.10 bzoj1499: [NOI2005]瑰丽华尔兹(单调队列优化dp)

    传送门 单调队列优化dp好题. 这题其实很简单. 我们很容易想到一个O(T∗n∗m)" role="presentation" style="position: ...

  8. 2018.09.10 bzoj1855: [Scoi2010]股票交易(单调队列优化dp)

    传送门 单调队列优化dp好题. 有一个很明显的状态设置是f[i][j]表示前i天完剩下了j分股票的最优值. 显然f[i][j]可以从f[i-w-1][k]转移过来. 方程很好推啊. 对于j<kj ...

  9. 【bzoj1855】 [Scoi2010]股票交易 单调队列优化DP

    上一篇blog已经讲了单调队列与单调栈的用法,本篇将讲述如何借助单调队列优化dp. 我先丢一道题:bzoj1855 此题不难想出O(n^4)做法,我们用f[i][j]表示第i天手中持有j只股票时,所赚 ...

随机推荐

  1. 深入理解xLua热更新原理

    热更新简介 热更新是指在不需要重新编译打包游戏的情况下,在线更新游戏中的一些非核心代码和资源,比如活动运营和打补丁.热更新分为资源热更新和代码热更新两种,代码热更新实际上也是把代码当成资源的一种热更新 ...

  2. oracle数据处理之逻辑备份与恢复

    逻辑备份与恢复 17.1 传统的导入导出exp/imp:传统的导出导入程序指的是exp/imp,用于实施数据库的逻辑备份和恢复. 导出程序exp将数据库中的对象定义和数据备份到一个操作系统二进制文件中 ...

  3. Unit3:控件\布局

    控件 TextView <TextView android:layout_width="match_parent" android:layout_height="w ...

  4. nginx如何写日志

    写日志函数为ngx_log_error_core,位于src/core/ngx_log.c:89行核心代码如下:while (log) { if (log->log_level < lev ...

  5. GO练习题

    package main import( "fmt" ) func list(n int) { for i := 0; i <= n; i++ { fmt.Printf(&q ...

  6. Latex博客转载

    \[{e^{ix}=cosx+isinx} \] \[[博客地址](https://www.cnblogs.com/Sinte-Beuve/p/6160905.html) \]

  7. 如何使用NuGet package .nupkg文件?

    如果你本来就有.nupkg文件并且你只需要.dll文件的话,你可以通过打开.zip下的lib文件夹来获取. 例如:

  8. python-生成器(generation)

    阐述思路是:迭代(iteration).迭代器(iterator).生成器(generator). 迭代 迭代是重复反馈过程的活动,其目的通常是为了接近并到达所需的目标或结果.每一次对过程的重复被称为 ...

  9. 从GitHub建站迁移到服务器(Java环境)

    一.购买域名和服务器 域名:阿里云:lookabc.cn 服务器:腾讯云,学生价格便宜 二.域名解析 注意:由于域名和服务器不在同一家,需要域名迁入和迁出 三.搭建服务器环境 1.下载xftp6和xs ...

  10. spring整合(Junit、web)

    1.整合Junit (1)整合前的测试类代码 public class Test { public static void main(String[] args) { ApplicationConte ...