P2872 [USACO07DEC]Building Roads S【kruskal】
题目
思路
开始的思路就是使用kruskal不停选择最短的边加入最小生成树,但是我发现这道题不但没有给路径,而且知识给了一部分已经修建的路径,那么如何判断我们的最小生成树用不用某一条已经修好的边呢?是算上已经修建好的边之后答案最小还是有另外更小的路径呢??咋判断呢?
问题一:没有路径:这道题人家给的是点的坐标,有了最表就可以求解距离,也就是说图中任何一点到任何一点的距离我们现在可以求了
问题二:如何判断已修建的用不用:既然使用了kruskal算法,就需要将所有的边都求出来再进行比较,这时候我们就可以判断,如果此时某条边在计算它的长度时发现是已经修建好的,就把它的长度设为0
最后使用算法寻找就行
缺点
这种方法需要在所有的边中寻找,其实就是在一个完全图中寻找,当边数较大的时候适合使用prim而不是kruskal,但是本题数据较小,所以可以过
代码
#include<iostream> #include<cstdio> #include<algorithm> #include<string> #include<cstring> #include<vector> #include<cmath> using namespace std; #define maxm 1000001 #define maxn 1010 struct node { int from; int to; double dis; }e[maxm*2];//自己补的边 struct point { double x; double y; }list[maxn];//点集 bool cmp(struct node &a, struct node &b) { return a.dis < b.dis; } int father[maxn]; int road[maxn][maxn];//记录已经修好的路 int n, m, cnt = 0; double allcount = 0;//答案 int find(int x) { if (father[x] == x) return x; return father[x] = find(father[x]); } int kruskal() { sort(e, e + cnt, cmp); for (int i = 0; i < cnt; i++) { int tempx = find(e[i].from); int tempy = find(e[i].to); if (tempx == tempy)continue; allcount += e[i].dis; father[tempx] = tempy; } return allcount; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%lf%lf", &list[i].x, &list[i].y); for (int i = 1; i <= n; i++)father[i] = i; for (int i = 1; i <= m; i++) { int a, b; scanf("%d%d",&a, &b); road[a][b] = road[b][a] = 1; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= i; j++)//这里只需要补一半的边 1 2 300 ; 2 1 300 对本题意义一样 { if (i == j)continue; e[cnt].from = j; e[cnt].to = i; if(road[i][j]==0)e[cnt].dis = sqrt((list[i].x - list[j].x)*(list[i].x - list[j].x) + (list[i].y- list[j].y)*(list[i].y-list[j].y)); else e[cnt].dis = 0; cnt++; } } kruskal(); printf("%.2lf", allcount); }