题目链接
https://cn.vjudge.net/problem/POJ-1751
题目大意
输入n个城市的坐标, 并输入m对城市已经连接, 请输入还要哪些城市连接使得任意两个城市互通, 并边权之和达到最小
"输入m对城市已经连接" 这地方我理解错了, 我以为这m对城市直接相连, 于是我原本写的是
for (int i = 1; i <= m; i++)
{
int a, b;
cin >> a >> b;
fa[a] = fb;
}
其实意思是这m对城市互通, 直接或间接. 所以应该是
for (int i = 1; i <= m; i++)
{
int a, b;
cin >> a >>b;
int fa = find(a), fb = find(b);
if (fa!=fb)
fa[fa] = fb;
}
还有一个地方我也写错了, 我以为m就是刚开始连接的边数, 其实m里面是可以有重复性的, 这也是我上面为什么加上 if(fa!=fb)
举个例子,有部分城市如图连接
m的输入可以是
3 2
2 5
1 2
2 4
1 5
第5行 1 5 是不是就没有意义了, 因为由前4行可以得出1 5 是相通的
所以kruskal判断循环结束的时候就出错了, 我原本写的是(第12行错误)
void kruskal()
{
int num = 0;
for (int i = 0; i < s; i++)
{
int fa = find(e[i].from), fb = find(e[i].to);
if (fa != fb)
{
cout << e[i].from << ' ' << e[i].to << endl;
per[fa] = fb;
num++;
if (num + m == n - 1) break;
}
}
}
m是原本已经通的边数, num是新添的边数, 根据树的性质: 边数=顶点数-1 , 所以num+m==n-1 不就没错了?! , 其实不然, m的意思并不是这个意思, 正如我上面举得例子, m=5, 其实只有4条边相通.
题目分析
本题就是一个最小生成树的问题, 我采用的是kruskal法, 想方设法得到三大要素 e[i[.from, e[i].to, e[i].val, 套图模板即可
代码示例
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#define N 10010
using namespace std;
const int maxn =5000000+5;
int per[N],n,m,s,res;
struct vet{ int x, y; }v[N];
struct node{ int from, to, val; }e[maxn];
bool cmp(node a, node b){ return a.val < b.val; }
int find(int x){ return per[x] == x ? x : per[x] = find(per[x]); }
void kruskal()
{
int num = 0;
for (int i = 0; i < s; i++)
{
int fa = find(e[i].from), fb = find(e[i].to);
if (fa != fb)
{
cout << e[i].from << ' ' << e[i].to << endl;
per[fa] = fb;
}
}
}
int main()
{
cin >> n ;
for (int i = 1; i <= n; i++)
{
per[i] = i;
cin >> v[i].x >> v[i].y;
}
cin >> m;
for (int i = 1; i <= m; i++)
{
int a, b;
cin >> a >>b;
int fa = find(a), fb = find(b);
if (fa!=fb)
per[fa] = fb;
}
s = 0;
for (int i = 1; i <= n;i++)
for (int j = i + 1; j <= n; j++)
{
if (find(i) == find(j)) continue;
e[s].from = i;
e[s].to = j;
e[s++].val = (v[i].x - v[j].x)*(v[i].x - v[j].x) + (v[i].y - v[j].y)*(v[i].y - v[j].y);
}
sort(e, e + s, cmp);
kruskal();
return 0;
}