1002 搭桥-最小生成树 图论

题目地址:http://codevs.cn/problem/1002/

这道题考察最小生成树和图的相关知识, 用到了二维到一维的转换(n行m列 坐标转化:(i,j)->  i*m+j ) 进一步利用一维数组做并查集处理,在搭桥过程中 要根据距离来搭桥,搭桥后要进行合并(注意 n个建筑物,n-1座桥就行了)。

题目描述 Description

有一矩形区域的城市中建筑了若干建筑物,如果某两个单元格有一个点相联系,则它们属于同一座建筑物。现在想在这些建筑物之间搭建一些桥梁,其中桥梁只能沿着矩形的方格的边沿搭建,如下图城市1有5栋建筑物,可以搭建4座桥将建筑物联系起来。城市2有两座建筑物,但不能搭建桥梁将它们连接。城市3只有一座建筑物,城市4有3座建筑物,可以搭建一座桥梁联系两栋建筑物,但不能与第三座建筑物联系在一起。

输入描述 Input Description

在输入的数据中的第一行包含描述城市的两个整数r 和c, 分别代表从北到南、从东到西的城市大小(1 <= r <= 50 and 1 <=  c <= 50). 接下来的r 行, 每一行由c 个(“#”)和(“.”)组成的字符.
每一个字符表示一个单元格。“#”表示建筑物,“.”表示空地。

输出描述 Output Description

在输出的数据中有两行,第一行表示建筑物的数目。第二行输出桥的数目和所有桥的总长度。

样例输入 Sample Input

样例1

3 5

#...#

..#..

#...#

样例2

3 5

##...

.....

....#

样例3

3 5

#.###

#.#.#

###.#

样例4:

3 5

#.#..

.....

....#

样例输出 Sample Output

样例1

5

4 4

样例2

2

0 0

样例3

1

0 0

样例4

3

1 1

AC代码如下,根据题解思考后,我在代码中加上了相应的注释,以帮助理解。毕竟是人家的代码,所以还是要敲上20遍,才能彻底成为自己的。

/*
1002 搭桥
http://codevs.cn/problem/1002/
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

char cc[60][60]; // 存储map
int n, m;
int ans = 0;  // ans 记录合并后桥的数目
bool k[60][60];
int fa[4000]; // 并查集 fa
int sum = 0;
int t = 1;

struct veda
{
	int pre;
	int re;
	int val;
}dis[100001];

int find(int x)
{
	if (fa[x] == x)
		return x;
	return fa[x] = find(fa[x]);
}

// 递归调用 并查集合并相连的建筑物
void junk(int a, int b)
{
	k[a][b] = 1;
	// 下面的方格
	if (cc[a + 1][b] == '#' && k[a + 1][b] == 0)
	{
		fa[a*m + b] = find((a - 1)*m + b); // 合并
		junk(a + 1, b);
	}
	//右
	if (cc[a][b + 1] == '#'&&k[a][b + 1] == 0)
	{
		fa[(a - 1)*m + (b + 1)] = find((a - 1)*m + b);
		junk(a, b + 1);
	}
	// 上
	if (cc[a - 1][b] == '#'&&k[a - 1][b] == 0)
	{
		fa[(a - 2)*m + b] = find((a - 1)*m + b);
		junk(a - 1, b);
	}
	// 左
	if (cc[a][b - 1] == '#'&&k[a][b - 1] == 0)
	{
		fa[(a - 1)*m + b - 1] = find((a - 1)*m + b);
		junk(a, b - 1);
	}
	// 左上
	if (cc[a - 1][b - 1] == '#'&&k[a - 1][b - 1] == 0)
	{
		fa[(a - 2)*m + b - 1] = find((a - 1)*m + b);
		junk(a - 1, b - 1);
	}
	// 右下
	if (cc[a + 1][b + 1] == '#'&&k[a + 1][b + 1] == 0)
	{
		fa[a*m + b + 1] = find((a - 1)*m + b);
		junk(a + 1, b + 1);
	}
	//左下
	if (cc[a + 1][b - 1] == '#'&&k[a + 1][b - 1] == 0)
	{
		fa[a*m + b - 1] = find((a - 1)*m + b);
		junk(a + 1, b - 1);
	}
	//右上
	if (cc[a - 1][b + 1] == '#'&&k[a - 1][b + 1] == 0)
	{
		fa[(a - 2)*m + b + 1] = find((a - 1)*m + b);
		junk(a - 1, b + 1);
	}
}

int cmp(veda a, veda b)
{
	if (a.val != 0 && b.val != 0)
		return a.val < b.val;
	if (a.val == 0)
		return false;
	if (b.val == 0)
		return true;
}

// 搭桥过程 每一个点 有6条线可以搭桥
// 本行 本列  紧挨着的行和列 共6个
void add(int a, int b)
{
	for (int i = 1; i <= n; ++i)
		if (i != a&&cc[i][b] == '#')
		{
			sum++;
			dis[sum].pre = (a - 1)*m + b; // 桥的起点 坐标 (i,j)转换成为 i*m+j
			dis[sum].re = (i - 1)*m + b; // 桥的终点 坐标
			dis[sum].val = abs(i - a) - 1; // 代表桥的长度
		}
	for (int i = 1; i <= n; ++i)
		if (i != a&&cc[i][b + 1] == '#')
		{
			sum++;
			dis[sum].pre = (a - 1)*m + b;
			dis[sum].re = (i - 1)*m + b + 1;
			dis[sum].val = abs(i - a) - 1;
		}
	for (int i = 1; i <= n; ++i)
		if (i != a&&cc[i][b - 1] == '#')
		{
			sum++;
			dis[sum].pre = (a - 1)*m + b;
			dis[sum].re = (i - 1)*m + b - 1;
			dis[sum].val = abs(i - a) - 1;
		}
	for (int j = 1; j <= m; ++j)
		if (j != b&&cc[a][j] == '#')
		{
			sum++;
			dis[sum].pre = (a - 1)*m + b;
			dis[sum].re = (a - 1)*m + j;
			dis[sum].val = abs(j - b) - 1;
		}
	for (int j = 1; j <= m; ++j)
		if (j != b&&cc[a + 1][j] == '#')
		{
			sum++;
			dis[sum].pre = (a - 1)*m + b;
			dis[sum].re = a*m + j;
			dis[sum].val = abs(j - b) - 1;
		}

	for (int j = 1; j <= m; ++j)
		if (j != b&&cc[a - 1][j] == '#')
		{
			sum++;
			dis[sum].pre = (a - 1)*m + b;
			dis[sum].re = (a - 2)*m + j;
			dis[sum].val = abs(j - b) - 1;
		}
}

int main()
{
	//freopen("in", "r", stdin);
	scanf("%d%d", &n, &m);
	// 1- n*m 表示每一个方格 fa置为其位置
	for (int i = 1; i <= n*m; ++i)
		fa[i] = i;
	//输入 n行m列
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			cin >> cc[i][j];
	// 并查集 合并 得到合并后的建筑物数量
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			if (cc[i][j] == '#') // 是'#' 且没有被访问过
			{
				if (k[i][j] == 0)
				{
					k[i][j] = 1;
					ans++;
					junk(i, j); // 合并所有能 合并的建筑物
				}
			}
	// 遍历# 进行搭桥工作
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j)
			if (cc[i][j] == '#')
			{
				add(i, j);
			}
	sort(dis + 1, dis + sum + 1, cmp); // 按照距离从小到大排序
	int g = 1;
	int rr = 0; // 桥的数目
	int minn = 0; //桥的总长度
	for (int i = 1; i <= sum; ++i) // ans个建筑 最多需要 ans-1 座桥 选择其中最短距离的ans-1个桥就行了
	{
		if (g == ans)
			break;
		int x = find(dis[i].pre);
		int y = find(dis[i].re);
		if (find(x) != find(y)) // 如果这两个# 不在一个并查集中 才能算作一个有意义的桥
		{
			fa[x] = y; // 将其合并  桥的数量 桥的长度
			rr++;
			minn += dis[i].val;
			g++;
		}
	}

	printf("%d\n%d %d\n", ans, rr, minn);
	return 0;
}
/*
3 5
#...#
..#..
#...#
3 5
##...
.....
....#
3 5
#.###
#.#.#
###.#
3 5
#.#..
.....
....#

5
4 4
2
0 0
1
0 0
3
1 1
*/

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-08 22:00:32

1002 搭桥-最小生成树 图论的相关文章

codevs 1002 搭桥

题目描述 Description 有一矩形区域的城市中建筑了若干建筑物,如果某两个单元格有一个点相联系,则它们属于同一座建筑物.现在想在这些建筑物之间搭建一些桥梁,其中桥梁只能沿着矩形的方格的边沿搭建,如下图城市1有5栋建筑物,可以搭建4座桥将建筑物联系起来.城市2有两座建筑物,但不能搭建桥梁将它们连接.城市3只有一座建筑物,城市4有3座建筑物,可以搭建一座桥梁联系两栋建筑物,但不能与第三座建筑物联系在一起. 输入描述 Input Description 在输入的数据中的第一行包含描述城市的两个

wikioi 1002 搭桥

题意:这题刚开始看错题意了,原来桥是建在一条直线上就行,不管距离多远. 思路:dfs求第一问答案,然后最小生成树搞,不能建桥的边就设为INF就行了,然后如果用到INF的边就加上0就行了.这样跑一遍最小生成树就是答案. #pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cstdio> #include<cstring> #include

搭桥(最小生成树)

codevs——1002 搭桥 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 有一矩形区域的城市中建筑了若干建筑物,如果某两个单元格有一个点相联系,则它们属于同一座建筑物.现在想在这些建筑物之间搭建一些桥梁,其中桥梁只能沿着矩形的方格的边沿搭建,如下图城市1有5栋建筑物,可以搭建4座桥将建筑物联系起来.城市2有两座建筑物,但不能搭建桥梁将它们连接.城市3只有一座建筑物,城市4有3座建筑物,可以搭建一座桥梁联系两栋建筑物,

1、Codevs 必做:2833、1002、1003、2627、2599

2833 奇怪的梦境 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description Aiden陷入了一个奇怪的梦境:他被困在一个小房子中,墙上有很多按钮,还有一个屏幕,上面显示了一些信息.屏幕上说,要将所有按钮都按下才能出去,而又给出了一些信息,说明了某个按钮只能在另一个按钮按下之后才能按下,而没有被提及的按钮则可以在任何时候按下.可是Aiden发现屏幕上所给信息似乎有矛盾,请你来帮忙判断. 输入描述 Input Description

1078 最小生成树

1078 最小生成树 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 白银 Silver 题解 查看运行结果 题目描述 Description 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当然,他需要你的帮助. 约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场.为了使花费最少,他想铺设最短的光纤去连接所有的农场. 你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案. 每两个农场

codevs1002 搭桥

1002 搭桥 题目描述 Description 有一矩形区域的城市中建筑了若干建筑物,如果某两个单元格有一个点相联系,则它们属于同一座建筑物.现在想在这些建筑物之间搭建一些桥梁,其中桥梁只能沿着矩形的方格的边沿搭建,如下图城市1有5栋建筑物,可以搭建4座桥将建筑物联系起来.城市2有两座建筑物,但不能搭建桥梁将它们连接.城市3只有一座建筑物,城市4有3座建筑物,可以搭建一座桥梁联系两栋建筑物,但不能与第三座建筑物联系在一起. 输入描述 Input Description 在输入的数据中的第一行包

训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分)

layout: post title: 训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分) author: "luowentaoaa" catalog: true mathjax: true tags: - 最小生成树 - 图论 - 训练指南 Stream My Contest UVA - 11865 二分带宽,然后判断最小生成树是否小于cost值. #include<bits/stdc++.h> using namespace std; typede

7、8月刷题总结

准备开学了囧,7.8月刷题记录,以后好来复习,并且还要好好总结! 数据结构: splay: [BZOJ]1503: [NOI2004]郁闷的出纳员(Splay) [BZOJ]1269: [AHOI2006]文本编辑器editor(Splay) [BZOJ]1507: [NOI2003]Editor(Splay) treap: [BZOJ]1862: [Zjoi2006]GameZ游戏排名系统 & 1056: [HAOI2008]排名系统(treap+非常小心) [BZOJ]3224: Tyvj

2596 售货员的难题

2596 售货员的难题 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 某乡有n个村庄(1<n<=15),有一个售货员,他要到各个村庄去售货,各村庄之间的路程s(0<s<1000)是已知的,且A村到B村与B村到A村的路大多不同.为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1,他不知道选择什么样的路线才能使所走的路程最短.请你帮他选择一条最短的路. 输入描述 Inpu