把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF666D】Chain Reaction(暴搜+细节讨论)

点此看题面

  • 给定平面直角坐标系内四个顶点\(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(T\cdot2^4\cdot4!)\)

#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\n",ans),i=0;i^4;++i) printf("%d %d\n",sx[i],sy[i]);
	}return 0;
}
posted @ 2021-05-06 07:36  TheLostWeak  阅读(69)  评论(0编辑  收藏  举报