题目链接: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; }