Usaco 生气的奶牛 - (dp&滑窗)|(二分&贪心check)

生气的奶牛
题目描述
在数轴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)

这是他的blog链接

粘一个他的代码,珍藏一下

#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));
}

转载于:https://www.cnblogs.com/katarinayuan/p/6572811.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值