HDU 3622 Bomb Game 二分+2-sat

http://acm.hdu.edu.cn/showproblem.php?pid=3622

题意:

给定n个回合,每个回合给你两个点,每个回合只能选择一个点放置炸弹,在n个回合里选出n个点放置炸弹,炸弹的爆炸范围是一个圆形范围,半径可以控制。求满足每个炸弹爆炸的圆形区域不相交的条件下,的所有半径里面的最小半径最大值。

思路:

二分枚举两点之间的距离,如果存在两点距离小于等于枚举的距离时,则表示这两个点之间存在矛盾(i->j),然后根据2-sat构图方法建图,i->j'  ,  j' -> i  , j - >i'  , i '->j然会通过2-sat判断是否满足条件,如果存在矛盾说明枚举的距离太小,如果不存在说明没机的距离太大....

//#pragma comment(linker,"/STACK:327680000,327680000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))

#define ll long long
#define inf 0x7f7f7f7f
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define ll long long
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define lowbit(x)   (x)&(-x)
#define Read()  freopen("din.txt", "r", stdin)
#define Write() freopen("dout.txt", "w", stdout);


#define N 207
using namespace std;

const double eps = 1e-6;

struct point
{
    double x,y;
}pt[N];

double dis[N][N];
int mat[N][N];

int dfn[N],low[N];
int belong[N],stk[N];
bool isn[N];

int cnt,idx,top;
int n;

int dblcmp(double x)
{
    if (x > eps) return 1;
    else if (x < -eps) return -1;
    else return 0;
}
double getR(int i,int j)
{
    double x = pt[i].x - pt[j].x;
    double y = pt[i].y - pt[j].y;
    return x*x + y*y;
}
void build(double mid)
{
    int i,j;
    CL(mat,0);

    for (i = 0; i < 2*n; ++i)
    {
        isn[i] = false;
        low[i] = dfn[i] = -1;
        belong[i] = 0;
        for (j = 0; j < 2*n; ++j)
        {
            if (i == j) continue;
            if (dis[i][j] <= mid*mid)//存在矛盾的点建图
            {
                if (j%2 == 0)
                {
                    mat[i][j + 1] = 1;
                   // mat[j + 1][i] = 1;
                }
                else
                {
                    mat[i][j - 1] = 1;
                   // mat[j - 1][i] = 1;
                }

                if (i%2 == 0)
                {
                    mat[j][i + 1] = 1;
                   // mat[i + 1][j] = 1;
                }
                else
                {
                    mat[j][i - 1] = 1;
                    //mat[i - 1][j] = 1;
                }
            }
        }
    }
    idx = top = cnt = 0;
}
void tarjan(int i)
{
    int j;
    dfn[i] = low[i] = ++idx;
    stk[++top] = i; isn[i] = true;
    for (j = 0; j < 2*n; ++j)
    {
        if (mat[i][j])
        {
            if (dfn[j] == -1)
            {
                tarjan(j);
                low[i] = min(low[i],low[j]);
            }
            else if (isn[j])
            {
                low[i] = min(low[i],dfn[j]);
            }
        }
    }
    if (dfn[i] == low[i])
    {
        ++cnt;
        do
        {
            j = stk[top--];
            isn[j] = false;
            belong[j] = cnt;
        }while (j != i);
    }

}
bool solve(double mid)
{
    int i;
    build(mid);
    //tarjan缩点
    for (i = 0; i < 2*n; ++i)
    {
        if (dfn[i] == -1) tarjan(i);
    }
    //2-sat判断是否存在矛盾
    for (i = 0; i < n; ++i)
    {
        if (belong[2*i] == belong[2*i + 1]) return false;
    }
    return true;
}
int main()
{
   //Read();
    int i,j;
    while (~scanf("%d",&n))
    {
        for (i = 0; i < 2*n; ++i) scanf("%lf%lf",&pt[i].x,&pt[i].y);

//        for (i = 0; i < 2*n; ++i) printf("%d %lf  %lf\n",i,pt[i].x,pt[i].y);
        double l = 0;
        double r = 0;
        for (i = 0; i < 2*n; ++i)
        {
            for (j = 0; j < 2*n; ++j)
            {
                if (i == j) dis[i][j] = 0;
                else
                {
                    dis[i][j] = getR(i,j);//计算距离的平方
                    r = max(r,dis[i][j]);
                }
            }
        }
        //printf(">>%lf\n",r);
        r = sqrt(r);
        double ans = 0,mid = 0;
        while (dblcmp(l - r) < 0)//二分枚举
        {
            mid = (l + r)/2.0;
            if (solve(mid))
            {
                l = mid;
                ans = mid;
            }
            else
            {
                r = mid;
            }
        }
        printf("%.2lf\n",ans/2.0);
    }
    return 0;
}

  

 

posted @ 2013-01-29 09:23  E_star  阅读(259)  评论(0编辑  收藏  举报