生气的奶牛
题目描述
在数轴x上摆放有n(2<=n<=50000)捆干草堆,没有任何两堆在同样的位置,所有的位置均为整数。你可以用弹弓射击射击数轴上的任意地点。如果你用弹弓以R的力度射击x处,那么该处会发生爆炸,爆炸的范围是以R为半径的圆形区域,所以它会使得[x-R,x+R]的所有干草堆同时发生爆炸。这些干草堆的爆炸半径是R-1。它们又会触发连锁反应,第三轮的爆炸的半径为R-2,依次递减。请选择最小的力度射击,使得所有的干草堆全部爆炸。
输入
第一行包含N。接下来N个整数,表示干草堆的位置。所有位置在[0,1000000000]内。
输出
输出最小的力度R,使得所有的干草堆发生爆炸。四舍五入保留一位小数。
样例输入
5
8 10 3 11 1
样例输出
3.0
提示
样例解释:
如果以力度3射击坐标5,则坐标3,坐标8处的干草堆会发生爆炸,然后又会引爆坐标1和坐标10的干草堆,最后引爆坐标11处的干草堆。
来源
usaco gold 2016.1
Solution 1:dp+滑窗
分析:
dp[][0]是一个单调不递减的数列,dp[][1]是一个单调不上升的数列。
就dp[][0]举例:
当距离(a[i]-a[x])较大,dp[x][0]+1较小的时候,由于求两者的较大值,很显然可以调小距离,相应的调大dp[x][0]+1,因此将x往右移。
如果没有这样的更优的x,就只有用max(dp[i-1][0],a[i]-a[i-1])了。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define INF 2000000000
#define MAXN 50000
int n,a[MAXN+10],dp[MAXN+10][2];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF){
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return ;
}
}
}
void read()
{
Read(n);
for(int i=1;i<=n;i++)
Read(a[i]);
sort(a+1,a+n+1);
}
void DP() //分别dp出把干草堆左面和后面的爆炸完要的最小R
{
memset(dp,0,sizeof dp);
for(int i=2,x=1;i<=n;i++){
while(x<i&&a[i]-a[x]>dp[x][0])
x++;
if(x!=i)
dp[i][0]=dp[x][0]+1;
else
dp[i][0]=max(dp[i-1][0],a[i]-a[i-1]);
}
for(int i=n-1,x=n;i>=1;i--){
while(x>i&&a[x]-a[i]>dp[x][1])
x--;
if(x!=i)
dp[i][1]=dp[x][1]+1;
else
dp[i][1]=max(dp[i+1][1],a[i+1]-a[i]);
}
}
void workout()
{
int i=1,j=n;
double ans=INF*1.0;
while(i<j){
ans=min(ans,max((a[j]-a[i])/2.0,min(dp[i][0],dp[j][1])+1.0));
if(dp[i+1][0]<dp[j-1][1])
i++;
else
j--;
}
printf("%.1lf\n",ans);
}
int main()
{
read();
DP();
workout();
return 0;
}
二分+贪心check(From Liu Junhao)
粘一个他的代码,珍藏一下
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 50000
int n,a[MAXN+10],d[MAXN+10];
void Read(int &x){
char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
void read(){
Read(n);
for(int i=1;i<=n;i++)
Read(a[i]);
sort(a+1,a+n+1);
}
bool check(double R){
double mxr,mir;
d[1]=0;
int i,j;
for(i=1;i<=n;){
if(i==n)
return 1;
for(j=i+1;a[j]-a[i]<=d[i]+1&&j<=n;j++);
if(j>i+1)
j--;
if(a[j]-a[i]>R-1){
mxr=a[i]+R;
break;
}
d[j]=max(d[i]+1,a[j]-a[i]);
i=j;
if(d[i]>R-2&&d[i]<=R-1){
mxr=a[i]+R;
break;
}
}
d[n]=0;
for(i=n;i;){
if(i==1)
return 1;
for(j=i-1;a[i]-a[j]<=d[i]+1&&j;j--);
if(j<i-1)
j++;
if(a[i]-a[j]>R-1){
mir=a[i]-R;
break;
}
d[j]=max(d[i]+1,a[i]-a[j]);
i=j;
if(d[i]>R-2&&d[i]<=R-1){
mir=a[i]-R;
break;
}
}
if(mxr>=mir)
return 1;
return 0;
}
double partition(double l,double r){
double mid;
int i=0;
while(++i<=70){
mid=(l+r)/2;
if(check(mid))
r=mid;
else
l=mid;
}
return l;
}
int main()
{
read();
printf("%.1lf\n",partition(1,1000000000));
}