原本以为昨天老师考的已经够难了,弄了那么多的状压DP但是今天老师好像比昨天还狠,弄的状压题比昨天还多,还难,还额外弄了两道没做过的额外的题,还弄了两道tarjan......
这道题虽然是第一道题,但却是我最后改的(考试的时候没做出来),其实看完题解后觉得这道题也没有那么难,看来以后这类类似的对dp式子进行分析的题还要多做.
分析: 读完题目后考虑递推公式,dp[i][j]=max(dp[i-1][k])+b[i]-|a[i]-j|在这个式子之中b[i]的值是已经确定的,i一旦确定a[i]也就没有问题了,所以我们把目光聚焦在d[i][j]上我们当然可以用一个多重循环来解决这个问题,但是根据题目中的数据范围这样写是一定会超时的,但是假设我们最后站在j点,设h=(t1-t2)*d,那么我们的k的活动范围就在j+h和j-h之间,区间移动求一个最值,我们想到来使用单调队列来进行优化就可以了,dp如果硬开是开不下的,但是只和上一行的状态有关,所以用滚动数组优化.
1 #include<cstring> 2 #include<algorithm> 3 #include<cstdio> 4 #include<queue> 5 typedef long long ll; 6 using namespace std; 7 const int maxn=15e4+5; 8 ll dp[2][maxn]; 9 void Solve(){ 10 int n,m,d; 11 scanf("%d%d%d",&n,&m,&d); 12 int t0=1,k=0; 13 while(m--){ 14 int a,t,b; 15 scanf("%d%d%d",&a,&b,&t); 16 ll h=(ll)1*(t-t0)*d; 17 h=min(h,(ll)n); 18 t0=t; 19 k=!k; 20 deque<int>q;//单调队列 21 for(int i=1,j=1;i<=n;++i){ 22 for(;j<=i+h&&j<=n;++j){ 23 while(!q.empty()&&dp[!k][q.back()]<=dp[!k][j]) q.pop_back(); 24 q.push_back(j); 25 } 26 while(!q.empty()&&q.front()<i-h) q.pop_front(); 27 dp[k][i]=dp[!k][q.front()]+b-abs(a-i); 28 } 29 } 30 ll ans=dp[k][1]; 31 for(int i=2;i<=n;++i) 32 ans=max(ans,dp[k][i]); 33 printf("%lld\n",ans); 34 } 35 int main(){ 36 //freopen("a.in","r",stdin); 37 Solve(); 38 return 0; 39 }