首页 > 代码库 > sgu128snack

sgu128snack

按我的理解大概说下题意:
有很多个点,最多一万个,告诉你它们的坐标。现在需要构造一条闭合线。这个闭合线满足以下六点:
1必须是闭合的。
2必须用到所有的点。
3构造的线段之间若成角,则必须是90度。
4构造的边必须与x或y轴平行。
5构造的线段之间不存在交错和重复的情况。
6最后形成的闭合线长度必须要是所有可能中最短的。

因为我觉得能不用复杂的数据结构就不用,能不写麻烦的算法就不写,所以估算了一下时间复杂度,发现可以
用暴力,于是,我的做法就是暴力。

下面是较详细的做法:

既然要构造一条线出来,直接构造的话,我不知道怎么搞,于是用分治的思想,每次弄两个点出来,看应不应该
把它们连起来,由于要求成角必须是90,又要线的长度最短,于是当两个点的横坐标或纵坐标相等,它们之间的
距离与它们之中任意一点跟它们之外的其它点的距离相比是最短的话,那么就可以相连。

实现这个过程,我是用两次排序排序,第一次排序,如果横坐标不等则按横坐标递增排序,否则按纵坐标递增排序,
第1个点跟第2个点相连,第3个点跟第4个点相连,以此类推。第二次排序,如果纵坐标不等则按纵坐标递增排序,
否则按横坐标递增排序,同样的,第1个点跟第2个点相连,第3个点跟第4个点相连,以此类推。

这样就把所有的点给连好了。

当然这样的话,结果不一定是一个闭合的线,可能有多个闭合线,当然也有可能没有闭合的线,或是有两条线段交错的情况。
所以还需要判断是否闭合以及唯一,也就是说,判断构造出的线段是否能形成一个连通图,这个用并查集就好。
也要判断是否有两条线段交错,这个的话,我直接枚举所有情况,我把横线跟竖线分开,然后枚举是否有横线跟竖线交错,
如果构造的图连通,那么最多有一万条边,横线跟竖线的数量也是一样多的,也就是最多各位五千,那么枚举的数量是
五千的平方,也就是25乘以10的6次方,题目给了250ms,所以时间上绰绰有余,不会超时。

于是判断之后,如果发现图是一个连通图,又没有线段交错的情况,那么就可以把构造好的闭合线长度输出来了。

 

我AC的代码如下:

 

#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
#define end {printf("0");return 0;}
int num_dot,num_x,num_y,father[10001];
struct node
{
	int x,y,no;
}dot[10001];
struct node1
{
	node s,e;
}sidex[10001],sidey[10001];
int sf(int i)
{
	while(i!=father[i])
		i=father[i];
	return i;
}
bool isconnex()
{
	int i,a,b,sum=0;
	for(i=0;i<num_dot;i++)
		father[i]=i;
	for(i=0;i<num_x;i++)
		father[sf(sidex[i].s.no)]=sf(sidex[i].e.no);
	for(i=0;i<num_y;i++)
		father[sf(sidey[i].s.no)]=sf(sidey[i].e.no);
	for(i=0;i<num_dot;i++)
		if(father[i]==i)
		{
			sum++;
			if(sum>1)
				return 0;
		}
	return 1;
}
bool cmp1(node a,node b)
{
	if(a.x!=b.x)
		return a.x<b.x;
	return a.y<b.y;
}
bool cmp2(node a,node b)
{
	if(a.y!=b.y)
		return a.y<b.y;
	return a.x<b.x;
}
int newside()
{
	int i,sum=0;
	num_x=num_y=0;
	sort(dot,dot+num_dot,cmp1);
	for(i=0;i<num_dot;i+=2)
		if(dot[i].x!=dot[i+1].x)
			return -1;
		else
		{
			sum+=dot[i+1].y-dot[i].y;
			sidex[num_x].s=dot[i];
			sidex[num_x].e=dot[i+1];
			num_x++;
		}
	sort(dot,dot+num_dot,cmp2);
	for(i=0;i<num_dot;i+=2)
		if(dot[i].y!=dot[i+1].y)
			return -1;
		else
		{
			sum+=dot[i+1].x-dot[i].x;
			sidey[num_y].s=dot[i];
			sidey[num_y].e=dot[i+1];
			num_y++;
		}
	return sum;
}
bool iscross()
{
	int i,j;
	for(i=0;i<num_x;i++)
		for(j=0;j<num_y;j++)
		    if(sidex[i].s.x>sidey[j].s.x&&sidex[i].s.x<sidey[j].e.x&&sidey[j].s.y>sidex[i].s.y&&sidey[j].s.y<sidex[i].e.y)
    		    return 1;
    return 0;
}
int main()
{
	int i,sum;
	scanf("%d",&num_dot);
	for(i=0;i<num_dot;i++)
	{
		scanf("%d%d",&dot[i].x,&dot[i].y);
		dot[i].no=i;
	}
	if(num_dot&1)
		end
	sum=newside();
	if(sum==-1)
		end
	if(isconnex()&&!iscross())
		printf("%lld",sum);
	else
		end
}