#bzoj3036#生气的奶牛(贪心 + 二分 or DP)

来源:互联网 发布:jquery1.72.min.js 编辑:程序博客网 时间:2024/05/17 01:44

3036: 生气的奶牛

时间限制: 1 Sec  内存限制: 128 MB

题目描述

在数轴x上摆放有n(2<=n<=50000)捆干草堆,没有任何两堆在同样的位置,所有的位置均为整数。你可以用弹弓射击射击数轴上的任意地点。如果你用弹弓以R的力度射击x处,那么该处会发生爆炸,爆炸的范围是以R为半径的圆形区域,所以它会使得[x-R,x+R]的所有干草堆同时发生爆炸。这些干草堆的爆炸半径是R-1。它们又会触发连锁反应,第三轮的爆炸的半径为R-2,依次递减。请选择最小的力度射击,使得所有的干草堆全部爆炸。

输入

第一行包含N。接下来N个整数,表示干草堆的位置。所有位置在[0,1000000000]内。

输出

输出最小的力度R,使得所有的干草堆发生爆炸。四舍五入保留一位小数。

样例输入

58 10 3 11 1

样例输出

3.0

提示

样例解释:

如果以力度3射击坐标5,则坐标3,坐标8处的干草堆会发生爆炸,然后又会引爆坐标1和坐标10的干草堆,最后引爆坐标11处的干草堆。


这题只能炸一次,首先会有一个的贪心的思路:

爆炸点一定会在间隔距离最大的两个草堆之间。


如果不是的话,这一段最大的距离将直接算入爆炸的范围R当中,而R一直在减小,所以R将必须大于等于这样一段距离,而这显然不可能优于在它们之间爆炸。


又来想,爆炸点在它们之间的什么位置呢?

只能是刚好在整数位置,或者是.5的地方。


如果不是中点,那么离得较远的那一方的距离将成为R的考虑因素,而(当前那一段中)较近一方的距离就不再起作用了。

当第一层爆炸中满足了较远方,较近方也一定能满足,而其它的层数则由双方共同决定,此处只考虑最近的一层爆炸


其实如果想不通也没关系,毕竟这题我们使用的是二分答案,以上两条性质,用Dp做这题的时候才会需要。

笔者这里并不想介绍DP的思路,因为自己没写,而且还不是特别理解,

这题因为数据水,所以很多人的DP即使没有讨论完全,没有写对,还是能过,笔者为大家提供一份朋友的正确代码。


来说说二分吧,好理解,也容易写。

先按位置排序

二分力度,从0,到A[N] - A[1]。

check(O(N)):

题中是要求从中间爆炸到两边,一定会炸到1和N,反过来想。

从1向N方向炸回去,每一次如果R + 1能炸到就用R + 1炸,不然就变化成R+两点之间距离,

在R不超过当前check的mid的情况下,看最远能走多远,向右能走到什么位置。

从N向1方向也如此跑一次。

看这两个区间[1, R1]和[L2, N]之间有没有交集,如果有,就说明当前mid是满足炸完的要求的,而且爆炸的位置就是两区间的交集中任意一点(请想一想为什么,这很重要)


到这里,此题用二分得到了完美解决(即使你没有推出那两条性质,还是可以做得出来)。

时间复杂度为O(N*log1e9)


Code:(二分)

#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>using namespace std;const int Max = 50000;const double eps = 0.0001;int N;int A[Max + 5];void getint(int & num){char c;int flg = 1;num = 0;while((c = getchar()) < '0' || c > '9')if(c == '-')flg = -1;while(c >= '0' && c <= '9'){num = num * 10 + c - 48;c = getchar();}num *= flg;}bool Check(double mid){double L = 0.0, R = A[N] * 1.0;double Rr = 0.0;int p;for(int i = 1; i <= N; ){if(i == N)return 1;p = i + 1;while(p <= N && A[p] - A[i] <= Rr + 1)++ p;if(p > i + 1)-- p;if(A[p] - A[i] > mid - 1){L = A[i] + mid;break;}Rr = max(Rr + 1, A[p] - A[i] + 0.0);i = p;if(Rr > mid - 2 && Rr <= mid - 1){L = A[i] + mid;break;}}Rr = 0.0;for(int i = N; i >= 1; ){if(i == 1)return 1;p = i - 1;while(p >= 1 && A[i] - A[p] <= Rr + 1)-- p;if(p < i - 1)++ p;if(A[i] - A[p] > mid - 1){R = A[i] - mid;break;}Rr = max(Rr + 1, A[i] - A[p] + 0.0);i = p;if(Rr > mid - 2 && Rr <= mid - 1){R = A[i] - mid;break;}}if(L >= R)return 1;return 0;}int main(){getint(N);for(int i = 1; i <= N; ++ i)getint(A[i]);sort(A + 1, A + 1 + N);double dn = 0.0, up = A[N] - A[1], mid, rt;while(up - dn >= eps){mid = (dn + up) / 2;if(Check(mid))rt = mid, up = mid;else dn = mid;}printf("%.1lf\n", rt);return 0;}

易错数据:

4

1 3 5 7

(3.0)


5

1 3 5 7 9

(3.0)


Code:(DP from a friend)

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>#define max(a,b) ((a)>(b)?(a):(b))#define MAXN 50005using namespace std; int n, num[MAXN];double f[MAXN][2], ans; int main(){    scanf("%d",&n);    for(int i=1;i<=n;++i)scanf("%d",&num[i]);    sort(num+1,num+n+1);    for(int i=2, p=1;i<=n;++i)    {        f[i][0]=max(f[i-1][0],num[i]-num[i-1]);        while(num[i]-num[p]>f[i][0]&&p<i)++p;        f[i][0]=max(f[i][0],f[p][0]+1);    }    for(int i=n-1, p=n;i;--i)    {        f[i][1]=max(f[i+1][1],num[i+1]-num[i]);        while(num[p]-num[i]>f[i][1]&&p>i)--p;        f[i][1]=max(f[i][1],f[p][1]+1);    }         ans=num[n];    for(int i=1;i<=n;++i)        ans=min(ans,max(f[i][0],f[i][1]));    double tmp, dis;    for(int i=2;i<n;++i)    {        dis=num[i]-num[i-1];        tmp=max(f[i][1],f[i-1][0]);        if(f[i][1]==f[i-1][0])        {            if(dis==1)tmp+=0.5;            else tmp=max(tmp+1,dis/2.0);        }        else if(dis==tmp+1)tmp+=0.5;        else if(dis>tmp+1)tmp=max(tmp+1,dis/2.0);        ans=min(ans,tmp);    }    printf("%.1lf\n",ans);    return 0;}




阅读全文
0 0