USACO 2016 January Contest Gold T1: Angry Cows

孤者浪人 提交于 2020-03-24 02:41:00

题目大意

奶牛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 } 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!