码迷,mamicode.com
首页 > 其他好文 > 详细

Watching Fireworks is Fun(CF372C)

时间:2020-06-25 13:33:20      阅读:68      评论:0      收藏:0      [点我收藏+]

标签:c++   滚动   定义   math   ++   单位   return   ret   front   

Watching Fireworks is Fun(CF372C)

Decscription

  • 一个节日将在一个镇的主街道上举行。 街道被分为\(n\)段,从左到右编号为\(1\)\(n\),每个相邻部分之间的距离为\(1\)

  • 节日里在主街道有 \(m\) 个烟花要放,第\(i(1\le i\le m)\)次烟花将在 \(t_i\) 时在 \(a_i\) 段燃放。 如果您在第 \(i\) 次发射时处于 \(x(1\le x \le n)\) ,您将获得幸福值 \(b_i-|a_i-x|\) (请注意,幸福价值可能是负值)

  • 您可以以单位时间间隔移动长度为 \(d\) 个长度单位,但禁止离开主要街道。 此外,在初始时刻(时间等于 \(1\) )你可以在街道的任意部分,并希望最大化从观看烟花获得的幸福总和。 找到最大的总幸福。

  • 请注意,两个或多个烟花可以同时发射

Input

  • 第一行包含三个整数\(n, m, d (1≤ n ≤150000; 1≤ m ≤300; 1≤ d ≤ n)\)

  • 接下来\(m\)行每行包含三个整数\(a_i, b_i, t_i (1≤ a_i ≤ n; 1≤ b_i ≤10^9; 1≤ t_i ≤10^9)\)。第\(i\)行描述第\(i\)次烟花发射

  • 确保满足条件\(t_i≤t_{i + 1}(1≤i<m)\)

Output

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

Sample Input

样例输入1:
50 3 1
49 1 1
26 1 4
6 1 10

样例输入2:
10 2 1
1 1000 4
9 1000 4

Sample Output

样例输出1:
-31

样例输出2:
1992
  • 分析

    • 如果 \(i\) 时位置确定,在 \(x_i\) ,则到下一个燃放时刻时最远能一定\(h=(t_{i+1}-t_i)*d\),则位置可能在$[x_i-h,x_i+h] $ 。

    • 所以该次燃放后可能获得的幸福总和由上一次位于\([j?h,j+h]\)处的\(2h+1\)种情形得到。

    • 定义:\(dp[i][j]\),表示第 \(i\) 次烟花燃放时位于 \(j\) 时所能获得的最大的幸福总和。

    • 动态转移方程:\(dp[i][j]=max(dp[i-1][k])+b[i]-|a[i]-j|,k\in [j-h,j+h]\)

      • \(i\) 次燃放时处于 \(j(1\le j\le n)\),那第 \(i-1\) 次燃放时必须能到达 \(j\) ,所以 \(j-h\le k \le j+h\)
      • 当前状态至于上一行的某个区间的最值有关,我们很快就能联想起用单调队列进行优化。
      • 当前行状态至于上一行的状态相关,果断滚动数组压维。
    • Code

      #include <bits/stdc++.h>
      typedef long long LL;
      const int maxn=15e4+5;
      LL dp[2][maxn];
      void Solve(){
      	int n,m,d;scanf("%d%d%d",&n,&m,&d);
      	int pre_t=1,k=0;//pre_t 记录上一个烟花的时刻,k滚动数组标记,k当前行,!k上一行
      	while(m--){
      		int a,b,t;scanf("%d%d%d",&a,&b,&t);
      		int h=(t-pre_t)*d;//从上一个时刻到这个烟花燃放时刻最远能走多远
      		pre_t=t;//为下一个时刻做准备
      		k=!k;//滚动数组标记
      		std::deque <int> q;
      		for(int i=1,j=1;i<=n;++i){//i枚举当前燃放时所处的位置,j表示上一个烟花时可能的为主	
      			for(;j<=i+h && j<=n;++j){//注意j在上面定义的,即当i改变位置时,j没有重新赋值
      				while(!q.empty() && dp[!k][q.back()]<=dp[!k][j])q.pop_back();
      				q.push_back(j);//当上一行j位置的dp值比队尾大时显然,队尾就没用了
      			}
      			while(!q.empty() && q.front()<i-h)q.pop_front();//保证左边范围
      			dp[k][i]=dp[!k][q.front()]+b-std::abs(a-i);
      		}
      	}
      	LL ans=dp[k][1];
      	for(int i=2;i<=n;++i)//看最后一个烟花时,你可能在1~n的任何一个位置
      		ans=std::max(ans,dp[k][i]);
      	printf("%lld\n",ans);
      }
      int main(){
      	Solve();
      	return 0;
      }
      

Watching Fireworks is Fun(CF372C)

标签:c++   滚动   定义   math   ++   单位   return   ret   front   

原文地址:https://www.cnblogs.com/hbhszxyb/p/13191368.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!