题目大意
奶牛Bessie设计了一个游戏:“愤怒的奶牛”。游戏的原型是:有一些可爆炸的草堆分布在一条数轴的某些坐标上,玩家用弹弓把一头奶牛发射到数轴上。奶牛砸到数轴上的冲击波会引发附近的草堆爆炸,而被引爆的草堆可能会引爆其他草堆。游戏的目标是玩家用一只奶牛炸掉所有的草堆。
有N (2≤N≤50,000) 个草堆在数轴的不同位置,坐标为x1,x2,….,xn。如果玩家以能量R把奶牛发射到坐标x,就会引爆半径R及以内的的草堆,即坐标范围[x−R,x+R]的草堆都会燃爆,每个被奶牛引爆的草堆又会2次引爆半径R-1及以内的的草堆,2次引爆的草堆又会3次引爆半径R-2及以内的的草堆...直到一次引爆后没有其他草堆被波及或半径为0。
现在只有1头奶牛,能量为R,请计算如果要引爆所有的草堆,最小的R是多少?
题目分析
观察数据范围,n为50000,一般会向O(nlogn)的复杂度思考,所以我们考虑进行二分。
我们先用 f[i] 记录以i为中心可以向左覆盖前i-1个点的最小半径。 再用 g[i] 记录以i为中心可以向右覆盖至第n个点的最小半径。
那么我们二分枚举第一次爆炸的半径r。
枚举i,即第i个草堆为这次爆炸的左边界,再在左边界到右边界的草堆中枚举j,如果f[i]+1<=r并且g[i]+1<=r,则说明这个方案可行。
考虑再次优化,因为 f数组,g数组 与 草堆坐标 的差都具有单调性(i越大,f[i]越大,反过来,i越小, g[i] 越大),而答案取其中的最大值(f[i]=max(f[now]+1,a[i]-a[now])),
所以对于 j > i, f[j]的最优决策点一定在f[i]的决策点的右边,所以就可以利用这个性质优化了。
(建议手动画图模拟)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=5e4+10; 4 const int Inf=0x3f3f3f3f; 5 int n; 6 bool ans1; 7 int a[MAXN]; 8 int f[MAXN],g[MAXN]; 9 int ans; 10 inline void Init(){ 11 f[1]=0;g[n]=0; 12 int now=1; 13 for(int i=2;i<=n;++i){ 14 while(a[i]-a[now]>=f[now]+1&&now<i-1) ++now; 15 if(f[now]+1>a[i]-a[now-1]) --now; 16 f[i]=max(f[now]+1,a[i]-a[now]); 17 // cout<<f[i]<<' '; 18 } 19 // puts(""); 20 now=n; 21 for(int i=n-1;i>=1;--i){ 22 while(a[now]-a[i]>=g[now]+1&&now>i+1) --now; 23 if(g[now]+1>a[now+1]-a[i]&&now<n) ++now; 24 g[i]=max(g[now]+1,a[now]-a[i]); 25 // cout<<g[i]<<' '; 26 } 27 // puts(""); 28 } 29 int main(){ 30 ans=Inf; 31 scanf("%d",&n); 32 for(int i=1;i<=n;++i) 33 scanf("%d",&a[i]); 34 sort(a+1,a+n+1); 35 Init(); 36 int now=1; 37 for(int i=1;i<=n;++i){ 38 while(a[i]-a[now]>=(f[now]+1)*2&&now<i-1) ++now; 39 if(a[i]-a[now-1]<(f[now]+1)*2) --now; 40 if(max(g[i]+1,max(f[now]+1,(a[i]-a[now])/2))<ans||(max(g[i]+1,max(f[now]+1,(a[i]-a[now])/2))==ans&&((a[i]-a[now])%2<ans1))){ 41 ans=max(g[i]+1,max(f[now]+1,(a[i]-a[now])/2)); 42 if((a[i]-a[now])/2>=f[now]+1&&(a[i]-a[now])/2>=g[i]+1) 43 ans1=(a[i]-a[now])%2; 44 else ans1=0; 45 } 46 } 47 printf("%d",ans); 48 if(ans1) printf(".5"); 49 else printf(".0"); 50 return 0; 51 }