题意:有m个士兵,t秒,你要带尽可能多的士兵从0去n+1,且他们不能被杀死。路上有一些陷阱,陷阱d[i]会杀死能力比它小的士兵,陷阱位置在l[i],当你走到r[i]时可以拆除它。每次你可以向左或者向右移动。自己不会被陷阱杀死,可以先去把陷阱拆除再回来带兵。
(1): 首先一个很简单的想法是带ai[i]大的士兵,这样的话比带ai[i]小的士兵更划算,这个应该想想就可以知道。
(2): 因为这个显然是具有单调性的,带前l+1个士兵所要花的时间肯定是要大于等于这个带前l个士兵所要花的时间的。所以这样我们就可以知道f(l)(l为带前l个士兵)在l这个变量上花的时间是不下降的,那么我们就可以用二分来做,这个其实也是很显然的,二分单调性解决 (有时候很难真正的去解释,只能说题感觉)
(3): 那么接下来回到一个新的问题,当我们带了这么多兵以后,怎么验证呢?这就是一个很简单的区间覆盖问题了,o(n)可以解决
下面是代码:
1 #include <cstdio> 2 #include <cmath> 3 #include <bitset> 4 #include <algorithm> 5 #include <iostream> 6 typedef long long ll; 7 using namespace std; 8 const int maxn=201000; 9 int m,n,k,t; 10 int ai[maxn]; 11 struct node{ 12 int li,ri,wi; 13 }; 14 node sum[maxn]; 15 16 bool cmp(node n1,node n2){ 17 return n1.li<n2.li; 18 } 19 20 bool scmp(int a,int b){ 21 return a>b; 22 } 23 24 void init(){ 25 scanf("%d%d%d%d",&m,&n,&k,&t); 26 for(int i=1;i<=m;i++) scanf("%d",&ai[i]); 27 sort(ai+1,ai+m+1,scmp); 28 for(int i=1;i<=k;i++) scanf("%d%d%d",&sum[i].li,&sum[i].ri,&sum[i].wi); 29 sort(sum+1,sum+k+1,cmp); 30 } 31 32 bool check(int mid){ 33 //如果没有人,直接返回true; 34 if(mid==0) return true; 35 //这个边界处理是大问题 36 //最后一个在循环外算 37 int res=0,l=-1,r=-1; 38 for(int i=1;i<=k;i++){ 39 if(sum[i].wi<=ai[mid]) continue; 40 if(l==-1){ l=sum[i].li;r=sum[i].ri;continue; } 41 if(sum[i].li<=r){ 42 r=max(sum[i].ri,r); 43 }else{ 44 res+=(r-l+1); 45 r=sum[i].ri;l=sum[i].li; 46 } 47 } 48 //这个是在循环外算的 49 //if(l!=-1) 是为了防止没有经过运算就输出 50 if(l!=-1) res+=(r-l+1); 51 return res*2+(n+1)<=t; 52 } 53 54 void solve(){ 55 //二分边界不要设错了,不要设错了!!!!!!!! 56 int lb=0,rb=m,ans=1; 57 while(lb<=rb){ 58 int mid=(lb+rb)/2; 59 if(check(mid)){ 60 ans=mid; 61 lb=mid+1; 62 }else rb=mid-1; 63 } 64 printf("%d ",ans); 65 } 66 67 int main(){ 68 //读入数据 69 init(); 70 //处理 71 solve(); 72 return 0; 73 }