1. 程式人生 > >2-Sat+二分_HDU 3622

2-Sat+二分_HDU 3622

題目意思是有n個選擇,每次都是隻能在兩個座標中選擇一個,然後再選擇的座標上畫圓,圓不能有重疊部分,問題畫的做大的面積是多少

題中是兩個座標選一個,然後約束條件就是在當前半徑下兩個點畫圓是否有重疊,所以這是一個2-Sat問題,但是我們的半徑應該怎麼確定呢?很容易想打二分列舉就好

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <vector>
#include <algorithm>

using namespace std;
const int MAXN = 41000;
const int  MAXM= 250;
int n;
struct Point{//存放每一次的選擇
    double x1,y1,x2,y2;
}point[MAXM];
struct Edge{//我們令一個選擇中的點是i(i<n),另一個是i+n
    int u,v,next;
}edge[MAXN];
int head[MAXM],t;
double dis[MAXM][MAXM];
int dfn[MAXM],low[MAXM],stact[MAXM],vis[MAXM],belong[MAXM],tot,index,num;
double Get_len(double a,double b,double c,double d){//兩點之間的距離
        return sqrt((a-c)*(a-c) + (b-d)*(b-d));
}

void Init()
{
    t = 0 ;tot = 0;index =0 ;num = 0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(stact,0,sizeof(stact));
    memset(vis,0,sizeof(vis));
    memset(belong,0,sizeof(belong));
}

void Add_edge(int u,int v)
{
    edge[t++].u = u;edge[t].v = v;edge[t].next = head[u];head[u] = t;
}

void Tarjan(int u)
{
    dfn[u] = low[u] = ++tot;
    stact[++index] = u;vis[u] = 1;
    for(int k = head[u];k != -1;k = edge[k].next)
    {
        int v=  edge[k].v;
        if(!dfn[v]){
            Tarjan(v);
            low[u] = min(low[u],low[v]);
        }
        else if(vis[v])
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u])
    {
        num ++;
        do{
            int v = stact[index];
            index -- ;
            belong[v] = num;
            vis[v] = 0;
        }while(u != stact[index+1]);
    }
}

bool Judge(double len)
{
    for(int i =0 ;i < n;i ++)
    {
        for(int j = i+1;j < n;j ++)//判斷約束條件
        {
            if(Get_len(point[i].x1,point[i].y1,point[j].x1,point[j].y1) < len*2)//兩點之間的距離小於半徑長度,那麼這兩個點就會產生矛盾
                Add_edge(i,j+n),Add_edge(j,i+n);
            if(Get_len(point[i].x1,point[i].y1,point[j].x2,point[j].y2) < len*2)
                Add_edge(i,j),Add_edge(j+n,i+n);
            if(Get_len(point[i].x2,point[i].y2,point[j].x1,point[j].y1) < len*2)
                Add_edge(i+n,j+n),Add_edge(j,i);
            if(Get_len(point[i].x2,point[i].y2,point[j].x2,point[j].y2) < len*2)
                Add_edge(i+n,j),Add_edge(j+n,i);
        }
    }
    for(int i = 0;i < 2*n;i ++)
        if(!dfn[i])
            Tarjan(i);
    for(int i =0 ;i < n;i ++)
        if(belong[i] == belong[i+n])
            return false;
    return true;
}
int main()
{
    //二分列舉+2-Sat判斷可行性
    while(~scanf("%d",&n))
    {
        for(int i =0 ;i < n;i ++)
            scanf("%lf%lf%lf%lf",&point[i].x1,&point[i].y1,&point[i].x2,&point[i].y2);

        double l = 0,r = 10000;
        while(r- l >= 0.001)
        {
            Init();//每次判斷都要初始化
            double mid = (l+r) / 2;
            if(Judge(mid)) l = mid;
            else r = mid;
        }
        printf("%.2lf\n",l);
    }
}

這裡想新增一點關於二分的東西,也就是兩個函式作用:

lower_bound :[m,n)區間返回第一個大於等於val的位置,不存在返回n

upper_bound:[m,n)區間返回第一個大於val的位置,不存在返回n