【JZOJ 6520】Fence Planning

闲话:

$ Upd 4.04 $

让我们

  • 哀悼抗击新冠肺炎疫情斗争牺牲烈士和逝世同胞

  • 致敬在这次斗争中默默付出的平凡英雄们

  • 纪念那些被 404 的声音

题目大意:

\(n\) 个点在一个棋盘上,一个点可以和多个点连一条边,其中有 \(m\) 条边,连成的点可以作一个小组,现在用一个长方形把任意一个小组框起来,那长方形的周长最小多长。

正文:

本题思路很简单,在求连通块的过程中,可以分别维护同一连通块中横纵坐标的最大值和最小值。本题可以用并查集和 DFS 做。

方法 1:

并查集。把难点讲细一点,在每次询问点时,判断点是否是小组(即连通块)边缘。

代码:

for(int i = 1; i <= n; i++) //询问
    check(i);               //判断
for(int i = 1; i <= n; i++)
    if(fa[i] == i) 
        ans = min(ans, 2 * ((b[i][1].x - b[i][2].x) + (b[i][1].y - b[i][2].y))); //更新答案(其中b[i][1].x,b[i][2].x,b[i][1].y,b[i][2].y分别表示i点所在的连通块中最大的x坐标、最小的x坐标、最大的y坐标、最小的y坐标)

其中判断的函数是:

inline void check(int x)
{
    b[i][1].x = max(b[i][1].x, a[x].x);
    b[i][2].x = min(b[i][2].x, a[x].x);
    b[i][1].y = max(b[i][1].y, a[x].y);
    b[i][2].y = min(b[i][2].y, a[x].y);
}

方法二:

用 dfs 暴力搜。dfs 的方法其实就是暴力,枚举到 \(i\) 点时,将它所在的连通块里的其他点搜了,同时找到连通块边缘的点。

代码:

for (int i = 1; i <= n; ++i)
	if(!vis[i])
	{
		build(a[i].x, a[i].y);  //初始化
		dfs(i);   // 搜索
		ans = min(ans, 2 * ((maxx - minx) + (maxy - miny))); //更新答案
	}

其中搜索和初始化的函数是:

inline void build(int a, int b)
{
	maxx = minx = a;
	miny = maxy = b;
}

inline void opt(int a, int b)
{
	maxx = max(maxx, a);
	minx = min(minx, a);
	maxy = max(maxy, b);
	miny = min(miny, b);
}

void dfs(int x)
{
	vis[x] = 1;
	opt(a[x].x, a[x].y);
	for (int i = head[x]; i; i = e[i].next)
		if(!vis[e[i].to]) dfs(e[i].to);
}
posted @ 2020-04-04 05:09  Jayun  阅读(126)  评论(0编辑  收藏  举报