题意:欲在平面直角坐标系上贴n个边长相同的正方形标签,要求标签不能重合。输入为n个点(给出坐标)。要求每个点是在标签的下边中点或者上边中点出,求符合题意的标签的最大边长。
思路:2-SAT+二分答案。
首先二分枚举答案。对每一对点,标签的位置由四种情况,枚举这四种情况,判断标签是否相交,如此建图。转化为tarjan算法解决的2-SAT问题。
#include <stdio.h> #include <string.h> #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define N 1005 struct edge{ int x,y,next; }e[N*N]; struct point{ int x,y; }p[N]; int n,T,top,lo,high,mid,res,index,num,tops; int first[N],dfn[N],low[N],strong[N],stack[N]; void init(){ top = index = num = 0; tops = -1; memset(first,-1,sizeof(first)); memset(dfn,-1,sizeof(dfn)); memset(strong,0,sizeof(strong)); } void add(int x,int y){ e[top].y = y; e[top].next = first[x]; first[x] = top++; } int test_cross(int i,int j,int len,int or1,int or2){//判断两个正方形是否相交 double x1_left = p[i].x - len/2.; double x1_right = p[i].x + len/2.; double x2_left = p[j].x - len/2.; double x2_right = p[j].x + len/2.; int y1_1 = p[i].y; int y1_2 = p[i].y + or1*len; int y2_1 = p[j].y; int y2_2 = p[j].y + or2*len; if((x1_right>x2_left) && (x2_right>x1_left) && max(y1_1,y1_2) > min(y2_1,y2_2) && max(y2_1,y2_2) > min(y1_1,y1_2) ) return 1; return 0; } void tarjan(int x){ int i; dfn[x] = low[x] = ++index; stack[++tops] = x; for(i=first[x];i!=-1;i=e[i].next){ if(dfn[e[i].y] == -1){ tarjan(e[i].y); low[x] = min(low[x],low[e[i].y]); }else if(!strong[e[i].y]) low[x] = min(low[x],dfn[e[i].y]); } if(dfn[x] == low[x]){ num++; do{ strong[stack[tops]] = num; }while(stack[tops--]!=x); } } int main(){ freopen("a.txt","r",stdin); scanf("%d",&T); while(T--){ int i,j; lo = 0;high = 10000;//二分答案 scanf("%d",&n); for(i = 1;i<=n;i++) scanf("%d %d",&p[i].x,&p[i].y); while(lo <= high){ init();//注意初始化的位置 mid = (lo+high)>>1; for(i = 1;i<n;i++)//建图 for(j = i+1;j<=n;j++){ if(test_cross(i,j,mid,-1,-1)) add(i,j+n),add(j,i+n); if(test_cross(i,j,mid,-1,1)) add(i,j),add(j+n,i+n); if(test_cross(i,j,mid,1,-1)) add(i+n,j+n),add(j,i); if(test_cross(i,j,mid,1,1)) add(i+n,j),add(j+n,i); } for(i = 1;i<=2*n;i++)//tarjan求强连通分量 if(dfn[i] == -1) tarjan(i); for(i = 1;i<=n;i++) if(strong[i] == strong[i+n])//如果有一个点的两种情况在同一个连通分量中(也就是必须同时满足) break; if(i > n){//答案合理 res = mid; lo = mid+1; }else high = mid-1; } printf("%d\n",res); } return 0; }