Watching Fireworks is Fun

题目链接:http://codeforces.com/problemset/problem/372/C

题意:城镇中有 n个位置,有 m个烟花要放。第 i个烟花放出的时间记为t[i],放出的位置记为 a[i]。如果烟花放出的时候,你处在位置 j,那么你将收获b[i]-abs(j-a[i])  点快乐值。

初始你可在任意位置,你每个单位时间可以移动不大于 d个单位距离。现在你需要最大化你能获得的快乐值。

思路:设dp[i][j] 表示在放第 i 个烟花时,你的位置在 j 所能获得的最大快乐值。状态转移方程 : dp[i][j]=max(dp[i-1][k])+b[i]-abs(a[i]-j);

由于n较大,直接这样dp肯定会超时,但可以用单调队列优化,时间复杂度变为O(n*m)。我们在计算一个新的 i 的状态值时候只需将原来的 dp[i-1] 构造成一个单调队列,并维护单调队列,使得其能在均摊O(1) 的时间复杂度内计算出 的值max(dp[i-1][k]),从而根据公式计算出 dp[i][j] 的值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 0xcfcfcfcfcfcfcfcf;
ll dp[2][200005];
ll a[310],b[310],t[310];
int que[200005];
int main()
{
    int n,m,d;
    cin>>n>>m>>d;
    for(int i=1;i<=m;i++)
        cin>>a[i]>>b[i]>>t[i];
    memset(dp,inf,sizeof(dp));
    memset(que,0,sizeof(que));
    for(int i=1;i<=n;i++)
        dp[0][i]=0;
    int k=1;
    for(int i=1;i<=m;i++)
    {
        int l=1,r=0;
        int h=1;
        for(int j=1;j<=n;j++)
        {
            for( ; h<=min(1ll*n,j+d*(t[i]-t[i-1]));h++)
            {
                while(l<=r&&dp[k^1][que[r]]<=dp[k^1][h])
                    r--;
                que[++r]=h;
            }
            while(l<=r&&que[l]<min(1ll*n,j-d*(t[i]-t[i-1])))
                l++;
            dp[k][j] = dp[k^1][que[l]]-abs(a[i]-j)+b[i];
        }
        k=k^1;
    }
    ll ans=inf;
    for(int i=1;i<=n;i++)
        ans=max(ans,dp[k^1][i]);
    cout<<ans<<endl;
}

 

posted @ 2020-09-29 22:07  ~zcb  阅读(148)  评论(0编辑  收藏  举报