- 给定平面直角坐标系内四个顶点(p_i),对于每个点选择与它横坐标相同或纵坐标相同的一个点(p'_i)。
- 要求(p'_i)是一个四边平行于坐标轴的正方形的四个顶点(不能退化成一点)。
- 求(max_{i=0}^3dis(p_i,p_i'))的最小值并构造一组方案。
- 数据组数(le50)
暴枚+初步分类讨论
首先,我们直接暴枚对于每个(p_i),(p_i')是与它横坐标相同还是纵坐标相同,由此便可以得到四条与坐标轴平行的取点直线。
显然,如果有超过两条相同的直线(正方形中不可能有三点某一维坐标相同)或某个方向上有超过两条不同的直线(正方形一个方向上只有两种坐标)肯定无解。
而若去重后两个方向上都恰有一条直线,实际上这也是无解的。
通过上面的分析,发现剩余的情况必然有至少一个方向上恰有两条不同的直线,则正方形的边长(d)就固定为这两直线间距离。
不妨假设是有两条与(x)轴平行的直线,那么只需分三类讨论就行了。
(2+2)
显然交点必选,只要判断组成的是不是正方形即可。
注意,这里有一个偷懒的写法,求出正方形的一种顶点方案后,我们不用直接判断谁与谁配对,而可以写个全排列暴搜,这样可以省去不少细节。这在之后的情况中也同样适用。
(2+1)
同理两个交点必选,然后正方形就只有左右两个。
两种情况分别搜一下就好了。
(2+0)
相当于可以任选一个夹在这两条直线之间的正方形。
对于一条直线上两点,肯定让正方形左边的点和左边的点配对,正方形右边的点和右边的点配对。
假设正方形左边的横坐标为(t),那么右边横坐标就是(t+d)。
到左边(x_L)的距离就是(|t-x_L|),而到右边(x_R)的距离就是(|t+d-x_R|=|t-(x_R-d)|)。
所以我们只要把两条直线上的(x_L,x_R-d)放在一起求出最小值和最大值,那么(t)的最优取值就是它俩的平均数。
代码:(O(Tcdot2^4cdot4!))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
using namespace std;
int ans,x[4],y[4],sx[4],sy[4],gx[4],gy[4],ox[4],oy[4],id[9];map<int,int> cx,cy;
int u[4];I void Match(CI i=0,CI t=0)//全排列暴搜匹配方式
{
if(t>=ans) return;if(i==4) {for(RI j=0;j^4;++j) sx[j]=ox[j],sy[j]=oy[j];return (void)(ans=t);}
for(RI j=0;j^4;++j) !u[j]&&(u[j]=1,ox[j]=gx[i],oy[j]=gy[i],
(x[j]==gx[i]||y[j]==gy[i])&&(Match(i+1,max(t,abs(x[j]-gx[i])+abs(y[j]-gy[i]))),0),u[j]=0);//至少有一维相同
}
int kx[2],ky[2];I void dfs(CI i,RI tx=0,RI ty=0)//暴枚每个点哪维相同
{
#define T(x) (id[x&-x])//状压的第一个点编号
#define P(x) (id[x^(x&-x)])//状压的第二个点编号
#define Work(x0,y0,x1,y1,x2,y2,x3,y3) (gx[0]=x0,gy[0]=y0,gx[1]=x1,gy[1]=y1,gx[2]=x2,gy[2]=y2,gx[3]=x3,gy[3]=y3,Match())//根据一组顶点方案开始搜索
if(i==4)//暴枚完了
{
RI d;if(tx==1&&ty==1) return;if(tx==2&&ty==2)//1+1(无解);2+2
{
abs(kx[0]-kx[1])==abs(ky[0]-ky[1])&&(Work(kx[0],ky[0],kx[0],ky[1],kx[1],ky[0],kx[1],ky[1]),0);return;//如果是正方形就去搜
}
if(ty==1) return d=abs(kx[0]-kx[1]),//1+2
Work(kx[0],ky[0],kx[1],ky[0],kx[0],ky[0]+d,kx[1],ky[0]+d),Work(kx[0],ky[0],kx[1],ky[0],kx[0],ky[0]-d,kx[1],ky[0]-d);//上下两个
if(tx==1) return d=abs(ky[0]-ky[1]),//2+1
Work(kx[0],ky[0],kx[0],ky[1],kx[0]+d,ky[0],kx[0]+d,ky[1]),Work(kx[0],ky[0],kx[0],ky[1],kx[0]-d,ky[0],kx[0]-d,ky[1]);//左右两个
if(!ty)//0+2
{
int tmp[4]={y[T(cx[kx[0]])],y[P(cx[kx[0]])],y[T(cx[kx[1]])],y[P(cx[kx[1]])]};
tmp[0]>tmp[1]&&(swap(tmp[0],tmp[1]),0),tmp[2]>tmp[3]&&(swap(tmp[2],tmp[3]),0),
d=abs(kx[0]-kx[1]),tmp[1]-=d,tmp[3]-=d,sort(tmp,tmp+4);RI t=tmp[0]+tmp[3]>>1;//取最小值和最大值的平均数
return Work(kx[0],t,kx[1],t,kx[0],t+d,kx[1],t+d);
}
if(!tx)//2+0
{
int tmp[4]={x[T(cy[ky[0]])],x[P(cy[ky[0]])],x[T(cy[ky[1]])],x[P(cy[ky[1]])]};
tmp[0]>tmp[1]&&(swap(tmp[0],tmp[1]),0),tmp[2]>tmp[3]&&(swap(tmp[2],tmp[3]),0),
d=abs(ky[0]-ky[1]),tmp[1]-=d,tmp[3]-=d,sort(tmp,tmp+4);RI t=tmp[0]+tmp[3]>>1;//取最小值和最大值的平均数
return Work(t,ky[0],t,ky[1],t+d,ky[0],t+d,ky[1]);
}
}
if(!P(cx[x[i]])&&(cx[x[i]]||tx^2)) !cx[x[i]]&&(kx[tx++]=x[i]),cx[x[i]]^=1<<i,dfs(i+1,tx,ty),!(cx[x[i]]^=1<<i)&&--tx;//不能有超过两条重合,同向不能超过两条不同
if(!P(cy[y[i]])&&(cy[y[i]]||ty^2)) !cy[y[i]]&&(ky[ty++]=y[i]),cy[y[i]]^=1<<i,dfs(i+1,tx,ty),!(cy[y[i]]^=1<<i)&&--ty;//不能有超过两条重合,同向不能超过两条不同
}
int main()
{
RI Tt,i;for(scanf("%d",&Tt),i=0;i^4;++i) id[1<<i]=i;W(Tt--)
{
for(i=0;i^4;++i) scanf("%d%d",x+i,y+i);if(ans=1e9,dfs(0),ans==1e9) {puts("-1");continue;};
for(printf("%d
",ans),i=0;i^4;++i) printf("%d %d
",sx[i],sy[i]);
}return 0;
}