Watching Fireworks is Fun(CF372C)

假如想象 提交于 2019-11-26 19:49:50

题目

A festival will be held in a town’s main street. There are n sections in the main street. The sections are numbered 1 through n from left to right. The distance between each adjacent sections is 1.
In the festival m fireworks will be launched. The i-th (1 ≤ i ≤ m) launching is on time ti at section ai. If you are at section x (1 ≤ x ≤ n) at the time of i-th launching, you’ll gain happiness value bi - |ai - x| (note that the happiness value might be a negative value).
You can move up to d length units in a unit time interval, but it’s prohibited to go out of the main street. Also you can be in an arbitrary section at initial time moment (time equals to 1), and want to maximize the sum of happiness that can be gained from watching fireworks. Find the maximum total happiness.
Note that two or more fireworks can be launched at the same time.

Input
The first line contains three integers n, m, d (1 ≤ n ≤ 150000; 1 ≤ m ≤ 300; 1 ≤ d ≤ n).
Each of the next m lines contains integers ai, bi, ti (1 ≤ ai ≤ n; 1 ≤ bi ≤ 109; 1 ≤ ti ≤ 109). The i-th line contains description of the i-th launching.
It is guaranteed that the condition ti ≤ ti + 1 (1 ≤ i < m) will be satisfied.

Output
Print a single integer — the maximum sum of happiness that you can gain from watching all the fireworks.
Please, do not write the %lld specifier to read or write 64-bit integers in C++. It is preferred to use the cin, cout streams or the %I64d specifier.

Examples
Input
50 3 1
49 1 1
26 1 4
6 1 10
Output
-31

Input
10 2 1
1 1000 4
9 1000 4
Output
1992

翻译下题目啦:o( ̄▽ ̄)ブ

节日将在镇的主要街道举行。主要街道有n个区域。这些部分从左到右编号为1到n。每个相邻部分之间的距离为1。
在节日期间,将推出烟花爆竹。第i(1≤i≤m)发射是在ai部分的时间ti。如果你在第i次发射时在x(1≤x≤n)处,你将获得幸福值bi - | ai - x | (请注意,幸福值可能是负值)。
您可以在单位时间间隔内移动最多d个单位,但禁止走出主要街道。此外,您可以在初始时刻(时间等于1)处于任意区域,并希望最大化从观看烟花中获得的幸福总和。找到最大的总幸福感。
请注意,可以同时启动两个或更多烟花。

输入
第一行包含三个整数n,m,d(1≤n≤150000;1≤m≤300;1≤d≤n)。
接下来的m行中的每一行包含整数ai,bi,ti(1≤ai≤n;1≤bi≤109;1≤ti≤109)。第i行包含第i次发射的描述。
保证满足条件ti≤ti+ 1(1≤i<m)。

输出
打印一个整数 - 您可以通过观看所有烟花获得的最大幸福总和。
请不要编写%lld说明符来读取或写入C ++中的64位整数。优选使用cin,cout流或%I64d说明符。

思路:这道题是个典型的单调队列优化DP的题目。这里我先给出学姐的讲解,然后谈谈自己的看法
在这里插入图片描述在这里插入图片描述
好,我们先分析下整体思路,我们按照时间线向后dp,每次我们都要枚举所有位置(时间不同的时候),来找出前一个时间段所能获得最大的幸福值,找的过程我们用单调队列来优化即可,最后随着时间线进行,得到结果~
然后再重点说下单调队列优化的这个过程(很重要的(๑•̀ㅂ•́)و✧)

for(ll j=1;j<=n;j++)//首先枚举区间,从1-n的每个点表示上一次的位置
{
  while(k<=n&&k<=j+d*t)//k表示这个点可以改变的范围,在时间t内最多走j+d*t
  {
   while(l<r&&f[1-cc][k]>=f[1-cc][q[r-1]])//我们开始筛选去除队列中的元素,因为我们要求最大值
    r--;//所以我们维护单调递增的队列,如果新加入的点k的f值大于原来的队列末尾,就删除,r--
   q[r++]=k++;//最后将新点k加入队列
  }
  while(l<r&&j-t*d>q[l])//这个时候再筛选队头了,不满足的去掉,因为一个点j
   l++;//能走的范围为 j-t*d<=l<=j+t*d  
  ll temp=s[i].b-abs(s[i].a-j);//最后得到这个点的最大值
  f[cc][j]=f[1-cc][q[l]]+temp;//就这样得到了每个点的最大值
}

个人感觉虽说dp,还是有点贪心的感觉(╮(╯▽╰)╭)
大概就是这么个情况,先找出dp式子,然后优化~

代码如下啦~(。・∀・)ノ

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
struct node{
   int a,b,t;
}s[350];//记录烟花信息 

ll n,m,d;
ll f[2][150010];//记录结果~分别是当前和上一次不同位置的最值 
ll q[150010];//队列

bool cmp(node x,node y)
{
   return x.t<y.t;
}

int main()
{
	ll time;//记录上一个时间
	scanf("%I64d%I64d%I64d",&n,&m,&d); 
    for(ll i=1;i<=m;i++)
    scanf("%I64d%I64d%I64d",&s[i].a,&s[i].b,&s[i].t);
    sort(s+1,s+1+m,cmp);
    memset(f,0,sizeof(f));
    time-s[1].t;
    ll cc=0;//滚动数组
	for(ll i=1;i<=m;i++)
	{
	  ll l=0,r=0,k=1;
	  if(s[i].t==time)
	  {
	    for(ll j=1;j<=n;j++)
		f[cc][j]=f[1-cc][j]+s[i].b-abs(s[i].a-j);
	  }
	  else
	  {
	  	ll t=s[i].t-time;
	  	time=s[i].t;
	  	for(ll j=1;j<=n;j++)
	  	{
	  		while(k<=n&&k<=j+d*t)
	  		{
	  			while(l<r&&f[1-cc][k]>=f[1-cc][q[r-1]])
	  			r--;
	  			q[r++]=k++;
	  		}
	  		while(l<r&&j-t*d>q[l])
	  		l++;
	  		ll temp=s[i].b-abs(s[i].a-j);
	  		f[cc][j]=f[1-cc][q[l]]+temp;
	  	}
	  }
	  cc=1-cc;
    }
    ll ans=-1e17;
    for(ll i=1;i<=n;i++)
    ans=max(ans,f[1-cc][i]);
    printf("%I64d",ans);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!