2020.04.01【NOIP提高组】模拟B【2.Fence Planning】

2020.04.01【NOIP提高组】模拟B【2.Fence Planning】

题目大意:
Description
Farmer John的NN头奶牛,编号为1…N(2≤N≤105),拥有一种围绕“哞网”,一些仅在组内互相交流却不与其他组进行交流的奶牛小组,组成的复杂的社交网络。每头奶牛位于农场的二维地图上的不同位置(x,y),并且我们知道有M对奶牛(1≤M<105)会相互哞叫。两头相互哞叫的奶牛属于同一哞网。

为了升级他的农场,Farmer John想要建造一个四边与x轴和y轴平行的长方形围栏。Farmer John想要使得至少一个哞网完全被围栏所包围(在长方形边界上的奶牛计为被包围的)。请帮助Farmer John求出满足他的要求的围栏的最小可能周长。有可能出现这一围栏宽为0或高为0的情况。

Input
输入的第一行包含N和M。以下N行每行包含一头奶牛的x坐标和y坐标(至多10^8的非负整数)。以下M行每行包含两个整数a和b,表示奶牛a和b之间有哞叫关系。每头奶牛都至少存在一个哞叫关系,并且输入中不会出现重复的哞叫关系。

Output
输出满足Farmer John的要求的围栏的最小周长。

Sample Input
7 5
0 5
10 5
5 0
5 10
6 7
8 6
8 4
1 2
2 3
3 4
5 6
7 6

Sample Output
10

分析:首先,大家应该都能想到要查找连通块在X轴方向上的长度与在Y轴方向上的长度的和的最小值的两倍。 似乎语言并不能很清晰地表达清楚。
化成式子就是ans=2*min(ans,maxx-minx+maxy-miny) 注意,这里的minx maxx miny maxy表达的是单个连通块的边缘
这里我用的是两遍并查集,标记连通关系,更新连通块的边缘坐标,然后查找每个连通块的边缘坐标,最后更新ans就可以了。

附上AC C++代码:

#include 
#include  

using namespace std;

int  father[1000005],maxx[1000005],maxy[1000005],minx[1000005],miny[1000005],x[1000005],y[1000005];
int getfather(int t)
{
	if (father[t]==t)
	{
		return t; 
	}
	return father[t]=getfather(father[t]);
}
void check(int t)
{
	int root=father[t];
	maxx[root]=max(maxx[root],x[t]);
	maxy[root]=max(maxy[root],y[t]);
	minx[root]=min(minx[root],x[t]);
	miny[root]=min(miny[root],y[t]);
}
int main()
{ 
	freopen("fenceplan.in","r",stdin);
	freopen("fenceplan.out","w",stdout);
	int n,ans,m,l,r;
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		minx[i]=0x3f3f3f3f;
		miny[i]=0x3f3f3f3f;
		father[i]=i;
	}
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&l,&r);
		int a=getfather(l);
		int b=getfather(r);
		father[a]=b;
	 } 
	 for (int i=1;i<=n;i++)
	 {
	 	check(i);
	 }
	 ans=0x3f3f3f3f;
	 for (int i=1;i<=n;i++)
	 {
	 	if (father[i]==i)
	 	{
	 		ans=min(ans,2*(maxx[i]-minx[i]+maxy[i]-miny[i]));
		 }
	 }
	printf("%d",ans);
	
	return 0;
	fclose(stdin);
	fclose(stdout);
} 

谢谢!

你可能感兴趣的:(2020.04.01【NOIP提高组】模拟B【2.Fence Planning】)