【題目描述】
CodeForces - 372CWatching Fireworks is Fun
題目的大概意思就是在一個編號為1…n的街道上現在按照時間順序放煙花,每個煙花獲得的幸福感為b?abs(a?x)b-abs(a-x)b?abs(a?x),x為觀看煙花的位置,為了提升我們的幸福感,我們可能會移動,每個時間單位可以移動d長度,現在問我們如果可以從任何一個地點開始觀看煙花,那么最后幸福感最大是多少
【題目分析】
連我這樣不太會DP的人都能看出來這是一個DP,按照放煙花的順序dp[i][j]=max(dp[i?1][k])+b?abs(a[i]?x)dp[i][j]=max ( dp[i-1][k] )+b-abs(a[i]-x)dp[i][j]=max(dp[i?1][k])+b?abs(a[i]?x),其中k為所有可以到達j位置的點,即i?t?d<=k<=i+t?di-t*d<=k<=i+t*di?t?d<=k<=i+t?d,t是距離上次放煙花的時間差
可是這樣做的話就需要對每一個煙花都遍歷一個很大的區間,應該會超時,所以我們需要進行優化。
我們對于每個煙花,我們都 用一個隊列從前往后計算每個位置,隊列中保存的是能到達當前位置的所有區域中幸福感最大的(按照從前往后的順序),如果后面某個位置的幸福感比前面的大,就會將前面的彈出,再將后面的放進去,因為對于再往后的位置來講,后面這個幸福感更大的位置更有用(前面的能到的后面的一定能到,后面能到的前面的不一定能到,而且前面的值還沒有后面的大,所以就不用考慮他了,這也算是一種貪心吧)
可能這樣說有點繞,可以先看代碼,注意理解雙重循環的部分,再回來看就應該很好理解了。
為了優化空間,我們用一個二維的數組滾動的保存數據,s0保存的是還沒有放這個煙花的幸福感,s1保存的是放了煙花后的幸福感,對于下一個煙花,將s0和s1調換就可以了,最后s0保存的就是最后的結果,查找最大值就可以了。
【AC代碼】
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<climits>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;typedef long long ll;
const int MAXN=150005;
ll dp[2][MAXN];
ll a,b,t,n,m,d,s0,s1,tt=1,step;int main()
{scanf("%lld%lld%lld",&n,&m,&d);s0=0; s1=1;while(m--){deque<int> q;scanf("%lld%lld%lld",&a,&b,&t);step=(t-tt)*d; tt=t;for(int i=1,j=1;i<=n;i++){for(;j<=i+step&&j<=n;j++){while(!q.empty() && dp[s0][q.back()]<=dp[s0][j]) q.pop_back(); //后面的值還比前面的大,前面的就沒用了q.push_back(j); //不管有沒有前面的彈出,后面的暫時都是有用的,除非更后面的將他擠出去}while(!q.empty() && q.front()<i-step) q.pop_front(); //如果隊列剛開始的地方已經不能到達位置i,就彈出。雖然可能他的幸福感很高,但是對后面的值已經沒有影響了。dp[s1][i]=dp[s0][q.front()]+b-abs(a-i);}swap(s0,s1);}ll ans=dp[s0][1];for(int i=2;i<=n;i++){if(dp[s0][i]>ans) ans=dp[s0][i];}printf("%lld",ans);return 0;
}
【參考博客】
https://www.cnblogs.com/yehs/p/11331813.html