【wikioi】1002 搭桥(dfs+最小生成树)

http://wikioi.com/problem/1002/

今天开始又开始刷水了哈T_T。照着hzwer神犇的刷题记录刷!!!

题解:

一开始我也不会,但是我想到了直接爆搜T_T。

好吧,题解。

首先对于第一个问,我们直接深搜就行了,沿着相连的城市走(ps,这里很坑啊啊啊,左上角和右上角还有左下角右下角也算联通啊!!!一开始我没发现!!)

那么我们就可以将这些城市看做缩点后的点集x。

然后我们再爆搜,依次从每个'#'点出发,向四个方向拓展(准确的说是四个方向,每个方向有3行(列)!!),将点集x中不同的点连边,在这里有个剪枝,如果拓展某个方向时,遇到了和自己点集一样的点,那么可以直接停止拓展。理由太简单了,自己想。

然后连完边后直接跑个最小生成树。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getnum()
#define print(a) printf("%d", a)
#define dbg(x) cout << #x << " = " << x << endl
inline int getnum() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
inline const int max(const int &a, const int &b) { return a>b?a:b; }
inline const int min(const int &a, const int &b) { return a<b?a:b; }
const int N=60;
const int dx[8]={0,0,1,1,1,-1,-1,-1}, dy[8]={1,-1,0,1,-1,0,1,-1};
struct ED { int x, y, w; }e[N*N*N];
int n, m, a[N][N], d[N][N], ans, sum, cnt, p[N*N];
inline const bool cmp(const ED &a, const ED &b) { return a.w<b.w; }
const int ifind(const int &x) { return x==p[x]?x:p[x]=ifind(p[x]); }

void dfs1(const int &x, const int &y) {
	int fx, fy;
	d[x][y]=ans;
	rep(i, 8) {
		fx=x+dx[i]; fy=y+dy[i];
		if(fx<1 || fx>n || fy<1 || fy>m || d[fx][fy] || !a[fx][fy]) continue;
		dfs1(fx, fy);
	}
}
void work1() {
	for1(i, 1, n) for1(j, 1, m) if(a[i][j] && !d[i][j]) { ++ans; dfs1(i, j); }
	printf("%d
", ans);
}
inline const bool insert(const int &fx, const int &fy, const int &x, const int &y, const int &w) {
	if(x<1 || x>n || y<1 || y>m || !d[x][y]) return 1;
	if(d[x][y]==d[fx][fy]) return 0;
	e[++cnt].x=d[fx][fy]; e[cnt].y=d[x][y];
	e[cnt].w=w-1;
	return 1;
}
void build(const int &x, const int &y) {
	for1(i, x+1, n) if(!insert(x, y, i, y, i-x) || !insert(x, y, i, y+1, i-x) || !insert(x, y, i, y-1, i-x)) break;
	for3(i, x-1, 1) if(!insert(x, y, i, y, x-i) || !insert(x, y, i, y+1, x-i) || !insert(x, y, i, y-1, x-i)) break;
	for1(i, y+1, m) if(!insert(x, y, x, i, i-y) || !insert(x, y, x+1, i, i-y) || !insert(x, y, x-1, i, i-y)) break;
	for3(i, y-1, 1) if(!insert(x, y, x, i, y-i) || !insert(x, y, x+1, i, y-i) || !insert(x, y, x-1, i, y-i)) break;
}
void work2() {
	for1(i, 1, n) for1(j, 1, m) if(a[i][j]) build(i, j);
	sort(e+1, e+1+cnt, cmp);
	for1(i, 1, ans) p[i]=i;
	sum=ans=0; int fx, fy;
	for1(i, 1, cnt) {
		fx=ifind(e[i].x); fy=ifind(e[i].y);
		if(fx!=fy) {
			p[fx]=fy;
			++ans;
			sum+=e[i].w;
		}
	}
	printf("%d %d
", ans, sum);
}

int main() {
	read(n); read(m); char c;
	for1(i, 1, n) for1(j, 1, m) {
		for(c=getchar(); c!='#'&&c!='.'; c=getchar());
		if(c=='#') a[i][j]=1;
	}
	work1();
	work2();
	return 0;
}

题目描述 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

数据范围及提示 Data Size & Hint

见描述

原文地址:https://www.cnblogs.com/iwtwiioi/p/3930818.html