2-Sat+二分_HDU 3622
阿新 • • 發佈:2019-02-07
題目意思是有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