洛谷 P3957 [NOIP2017 普及組] 跳房子(二分,單調佇列優化dp)
阿新 • • 發佈:2021-08-28
傳送門
第一年noip的考試題哇,滿滿的回憶
當年就因為這個題輸出-1騙了5分拿了普及一等
解題思路
先二分答案,很顯然答案滿足單調性。
然後就是一個從限定範圍轉移過來的線性dp,用單調佇列維護一下即可。
判斷-1可以根據所有的正數加起來是否大於等於k。
細節還是很多的:
- dp陣列初始化為一個很小的負數,防止有些跳不到的點在出現在了單調佇列裡
- 從零點開始列舉,這樣第一個點跳不到也沒問題
- 當求某個點dp值時發現佇列為空,說明沒有點能跳到這個點,continue即可(注意不是return 0)
- 注意更新佇列順序:先更新back,再更新head,再求當前點的dp值
- 注意每求得一個dp,都需要判斷一下是否滿足條件(題意中說可以隨時停下)
AC程式碼
#include<cstdio> #include<iostream> #include<cstring> #include<iomanip> #include<cmath> #include<algorithm> #include<deque> using namespace std; const int maxn=500005; const int maxx=1e9; int n,k,d,l,r=maxx,x[maxn],s[maxn]; long long sum,dp[maxn]; bool check(int mid){ deque<int> q; int ss=max(1,d-mid),t=min(maxx,d+mid),now=0; memset(dp,-0x3f,sizeof(dp)); dp[0]=0; for(int i=0;i<=n;i++){ while(now<i&&x[i]-x[now]>=ss){ while(!q.empty()&&dp[now]>dp[q.back()]) q.pop_back(); q.push_back(now); now++; } while(!q.empty()&&x[i]-x[q.front()]>t) q.pop_front(); if(q.empty()) continue; dp[i]=dp[q.front()]+s[i]; if(dp[i]>=k) return 1; } return 0; } int main(){ ios::sync_with_stdio(false); cin>>n>>d>>k; for(int i=1;i<=n;i++){ cin>>x[i]>>s[i]; if(s[i]>0) sum+=s[i]; } if(sum<k){ cout<<"-1"<<endl; return 0; } while(l<r){ int mid=(l+r)/2; if(check(mid)) r=mid; else l=mid+1; } cout<<l; return 0; }
//NOIP2017普及組 t4