Fork me on GitHub

UVALive 5000 Underwater Snipers --二分

题意:一条河岸线y=k,y>k区域有n个敌人,现在要在y<=k区域布置S个狙击手,狙击手的狙击范围为距离自己半径为D的圆内,问满足能够狙死所有的敌人的情况下,离河岸线最近的那个狙击手的离河岸线的最大距离是多少。

解法:求最小值最大的问题。二分这个距离,如果距离为x,那么最好是狙击手都站到y=k-x处,可以最大范围地打到敌人。

然后check的时候求出每个敌人在y=k-x线上的能够打到他的狙击手范围,为[Xi-sqrt(D^2-(Yi-k+x)*(Yi-k+x),Xi+sqrt(D^2-(Yi-k+x)*(Yi-k+x)],

然后就变成了区间选点问题,将所有的范围按右端点排序,扫一遍判断这么多区间需要布置多少个狙击手,最后如果需要的>S,那么check失败,否则成功。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define ll long long
using namespace std;
#define N 10007

struct node {
    ll a,b;
}p[N],seg[N];
int n,S;
ll k,D;

int cmp(node ka,node kb) { return ka.b < kb.b; }
bool check(ll x) {
    for(int i=1;i<=n;i++) {
        if(p[i].b-k+x > D) return false;
        double t = (double)(p[i].b-k+x);
        double delta = sqrt((double)D*D-t*t);
        seg[i].a = p[i].a - (ll)delta;
        seg[i].b = p[i].a + (ll)delta;
    }
    sort(seg+1,seg+n+1,cmp);
    int cnt = 1;
    ll R = seg[1].b;
    for(int i=2;i<=n;i++) {
        if(seg[i].a > R) {
            cnt++;
            R = seg[i].b;
        }
    }
    if(cnt > S) return false;
    return true;
}

int main()
{
    int t,cs = 1,i,j;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%d%d%lld",&k,&n,&S,&D);
        for(i=1;i<=n;i++) cin>>p[i].a>>p[i].b;
        ll low = 0, high = D;
        while(low <= high) {
            ll mid = (low+high)/2LL;
            if(check(mid)) low = mid+1;
            else high = mid-1;
        }
        printf("Case %d: ",cs++);
        if(high < 0) puts("IMPOSSIBLE");
        else printf("%lld\n",high);
    }
    return 0;
}
View Code

 

posted @ 2015-01-19 20:11  whatbeg  阅读(232)  评论(0编辑  收藏  举报