跳房子
阿新 • • 發佈:2019-01-08
連結:
https://www.luogu.org/problemnew/show/P3957
動態規劃,單調佇列+二分。
//my.cpp. #include <iostream> #include <stdio.h> #include <cstring> using namespace std; /* 定義dp[i]是跳到了第i個位置之後的最大得分。 那麼狀態轉移方程是; dp[i]=max(dp[j])+a[i]; //其中j是可以跳到i的位置。 如此選擇的原因是你思考的那種情況是不可能的。一定要符合事實。 */ long long MIN= 0x8080808080808080; const int inf=5e5+7; typedef long long ll; struct node { ll dis; ll score; }arr[inf]; ll que[inf],dp[inf]; //單調佇列。 int n,d,k; //不要在意細節,從主要的看起吧。定義一個變數??? 可以嗎? 1 long long fun(ll thel,ll ther) //一種是固定的,還有一種就是像這種不是固定的吧。 { memset(que,0,sizeof(que)); memset(dp,0x80,sizeof(dp)); dp[0]=0; int head=1,tail=0; // int point=0; // for(int i=1;i<=n;i++) // 關鍵是在dp[j],用一個變數來儲存 { while(arr[point].dis+thel<=arr[i].dis&&point<i) { if( dp[point]!=MIN ) //定義的是可以跳到其上。 { while(head<=tail&&dp[point]>=dp[que[tail]] )tail--; //理解還是關鍵啊。 que[++tail]=point; //如此看來,第一部分是新增加的元素進行擠原先佇列中的元素。 } point++; } //之後就是去除不在j的範圍中的了。對啊,這倆是相連的。 while( head<=tail&&arr[ que[head] ].dis+ther<arr[i].dis)head++; if(head<=tail) //對於這種不是一眼就能看出來的,其中的量是隨機的題目,不論何時都要進行判斷head是否小於等於tail。 dp[i]=dp[que[head]]+arr[i].score; } //之後是從哪一個範圍選出最大值呢? 應該是最大的吧。 long long ans=MIN; for(int i=1;i<=n;i++) if(dp[i]>ans) ans=dp[i]; return ans; } int main() { scanf("%d %d %d",&n,&d,&k); long long ans=0; for(int i=1;i<=n;i++) { scanf("%lld %lld",&arr[i].dis,&arr[i].score); if(arr[i].score>0) ans+=arr[i].score; } ll theans=0; if(ans<k) cout<<-1<<endl; else //定要嘗試最為精簡才行。 { ll l=0,r=max(arr[n].dis,(ll)d); while(l<=r) // { ll mid=(l+r)/2; ll thel=max( (int)1, int(d-mid)); //理解為是最小的距離吧。 ll ther=d+mid; long long ansans=fun(thel,ther); if(ansans>=k) { theans=mid; r=mid-1; } else { l=mid+1; } } cout<<theans<<endl; } return 0; }