题目大意
奶牛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 }
来源:https://www.cnblogs.com/LI-dox/p/11215834.html