题目大意
陶陶家的院子里有一棵苹果树,每到秋天树上就会结出$n$个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。
陶陶的手不能弯 (他仅能把手伸直),当且仅当陶陶达到的高度与苹果的高度相等的时候,陶陶才能摘到苹果。
好在陶陶有$m$个板凳,每个板凳的高度可以在区间$[l_i,r_i]$之间上下移动 (即可以随时变为该区间中任何一个值)。当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。
但是搬板凳对陶陶来说是一件费力的事情,所以他只能选择$k$个板凳来使用。
现在已知$n$个苹果到地面的高度,$m$个板凳的高度区间,陶陶能选择的板凳数$k$,以及陶陶把手伸直能达到的高度$h$,请帮陶陶算一下她最多能够摘到的苹果的数目。假设她碰到苹果,苹果就会掉下来。
题目分析
将所可以达到的高度区间计算出来后,问题变为:用最多$k$个区间覆盖最多的点。
而$m\le200$,显然可以使用资源分配类的动态规划解决。
设$f[i,j]$为前$j区间选择了$i个区间,第$j$个区间必选,所覆盖的点数。
先将区间按照右端点排序。
注意一下边界情况即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include<algorithm> #include<iostream> #include<iomanip> #include<cstring> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> #include<queue> using namespace std; inline const int Get_Int() { int num=0,bj=1; char x=getchar(); while(x<'0'||x>'9') { if(x=='-')bj=-1; x=getchar(); } while(x>='0'&&x<='9') { num=num*10+x-'0'; x=getchar(); } return num*bj; } const int maxn=1000005; int n,m,h,K,a[maxn],sum[maxn],f[205][205],ans=0,Ans=0; struct Segment { int l,r; bool operator < (const Segment& b) const { return r<b.r; } } b[maxn]; int main() { n=Get_Int(); m=Get_Int(); h=Get_Int(); K=Get_Int(); for(int i=1; i<=n; i++) { a[i]=Get_Int(); if(a[i]==h)ans++; else sum[a[i]]++; } for(int i=1; i<=1000000; i++)sum[i]+=sum[i-1]; for(int i=1; i<=m; i++) { b[i].l=Get_Int()+h; b[i].r=min(Get_Int()+h,1000000); if(b[i].l>1000000) { i--; m--; } } K=min(K,m); sort(b+1,b+m+1); for(int i=1; i<=K; i++) for(int j=1; j<=m; j++) for(int k=0; k<j; k++) { f[i][j]=max(f[i][j],f[i-1][k]+sum[b[j].r]-sum[max(b[j].l-1,b[k].r)]); Ans=max(Ans,f[i][j]); } printf("%d\n",Ans+ans); return 0; }
|