【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\n", 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\n", 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
见描述