CF-1055E:Segments on the Line (二分&揹包&DP優化)(nice problem)
You are a given a list of integers
You need to select exactly
The
InputThe first line contains four integers
You are a given a list of integers
You need to select exactly
The
InputThe first line contains four integers
The second line contains
Each of the next
It is possible that some segments coincide.
OutputPrint exactly one integer — the smallest possible
Examples Input4 3 2 2Output
3 1 3 2
1 2
2 3
4 4
2Input
5 2 1 1Output
1 2 3 4 5
2 4
1 5
1Input
5 3 3 5Output
5 5 2 1 1
1 2
2 3
3 4
-1
題意:給定給N個點,以及M個線段,讓你選擇S個線段,使得至少被一個線段覆蓋的點排序後,第K大最小,沒有則輸出-1。
思路:求第K大最小,顯然需要二分,每次驗證看當前的mid是否有大於等於K個數小於mid。驗證我們用dp來驗證,複雜度是O(NMS*lgN);
需要優化掉一個。這裡用揹包把M優化掉了,我們找到每個點的Next,Next代表包含這個點的最右端。就不難得到dp方程,這個時候M已經沒用了。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1510; struct in{ int L,R;}s[maxn]; int a[maxn],b[maxn],N,S,M,K,sum[maxn]; int dp[maxn][maxn],Next[maxn]; bool check(int Mid) //M個選最多S個的第K大 { rep(i,1,N) sum[i]=sum[i-1]+(a[i]<=Mid); rep(i,0,S) rep(j,0,N) dp[i][j]=0; rep(i,1,S){ rep(j,1,N) dp[i][j]=max(dp[i][j],dp[i-1][j]); //不選j位置。 rep(j,1,N) if(Next[j]) dp[i][Next[j]]=max(dp[i][Next[j]],dp[i-1][j-1]+sum[Next[j]]-sum[j-1]); //選j rep(j,1,N) dp[i][j]=max(dp[i][j],dp[i][j-1]); } return dp[S][N]>=K; } int main() { scanf("%d%d%d%d",&N,&M,&S,&K); rep(i,1,N) scanf("%d",&a[i]),b[i]=a[i]; rep(i,1,M) scanf("%d%d",&s[i].L,&s[i].R); rep(i,1,M) rep(j,s[i].L,s[i].R) Next[j]=max(Next[j],s[i].R); sort(b+1,b+N+1); int L=1,R=N,Mid,ans=-1; while(L<=R){ Mid=(L+R)>>1; if(check(b[Mid])) ans=b[Mid],R=Mid-1; else L=Mid+1; } printf("%d\n",ans); return 0; }