POJ 2421 Constructing Roads (最小生成树)
题意:给出邻接矩阵,在给出q条已经建好的路,让你再建一些路,使得连通所有的村庄并且兴建的路的长度时最小的。
思路:1.可以先将建好的路的端点合并,这里要注意的是,首先先要判断两个端点u、v的根节点是否相同,不同的话在合并。是为了防止有重复或者有环出现。
2.可以将建好的路的cost设为0,再直接用kruskal算法。由于建边的时候是用了上三角矩阵,所以很容易用端点u、v来推出相应边的编号。
代码1:
#include <iostream> #include <algorithm> #include <string.h> #include <stdio.h> #include <string> using namespace std; int n,q,cost,index,a,b; int ans; struct Edge{ int u,v; int cost; bool operator < (const Edge& tmp) const { return cost< tmp.cost; } }edge[5500]; struct UF{ int father[110]; void unit(){ for(int i=1;i<=n;i++){ father[i]=i; } } int find_root(int x){ if(father[x]!=x) father[x]=find_root(father[x]); return father[x]; } void Union(int fa,int fb){ father[fb]=fa; } }uf; int main() { while(scanf("%d",&n)!=EOF){ index=0; for(int i=1;i<=n;i++){ for(int j=1;j<=i;j++) scanf("%d",&cost); for(int j=i+1;j<=n;j++){ scanf("%d",&cost); edge[index].u=i; edge[index].v=j; edge[index].cost=cost; index++; } } sort(edge,edge+index); scanf("%d",&q); uf.unit(); int num=0; for(int i=1;i<=q;i++){ scanf("%d%d",&a,&b); int fa=uf.find_root(a); int fb=uf.find_root(b); //已经建好的,判断根节点是否一样,因为可能有重复,如果一样说明之前已经合并过,不一样的再合并 //也有可能已经建好的路中会有形成环的,所以也要判断一下 if(fa!=fb){ uf.Union(fa,fb); num++; } } int count=num; //统计边数 ans=0; for(int i=0;i<index;i++){ int u=edge[i].u; int v=edge[i].v; int fu=uf.find_root(u); int fv=uf.find_root(v); if(count>=n-1){ break; } if(fu!=fv){ ans+=edge[i].cost; count++; uf.Union(fu,fv); } } printf("%d\n",ans); } return 0; }
代码2:
#include <iostream> #include <algorithm> #include <string.h> #include <stdio.h> #include <string> using namespace std; int n,q,cost,index,a,b; int ans; int road[110][110];//road[a][b]=1表示a与b之间已经有路,防止已经建好的路中有重复 struct Edge{ int u,v; int cost; bool operator < (const Edge& tmp) const { return cost< tmp.cost; } }edge[5500]; struct UF{ int father[110]; void unit(){ for(int i=1;i<=n;i++){ father[i]=i; } } int find_root(int x){ if(father[x]!=x) father[x]=find_root(father[x]); return father[x]; } void Union(int fa,int fb){ father[fb]=fa; } }uf; int main() { while(scanf("%d",&n)!=EOF){ index=0; memset(road,0,sizeof(road)); for(int i=1;i<=n;i++){ for(int j=1;j<=i;j++) scanf("%d",&cost); for(int j=i+1;j<=n;j++){ scanf("%d",&cost); edge[index].u=i; edge[index].v=j; edge[index].cost=cost; index++; } } scanf("%d",&q); uf.unit(); //int num=0; for(int i=1;i<=q;i++){ scanf("%d%d",&a,&b); //防止有重复 if(road[a][b]==0){ road[a][b]=1; road[b][a]=1; //额,忘记edge数组的下标是从0开始的,所以idx还要最后-1。。。 //求出相应边的编号idx int idx=(2*n-a)*(a-1)/2+b-a-1; edge[idx].cost=0; } } sort(edge,edge+index); int count=0; //统计边数 ans=0; for(int i=0;i<index;i++){ int u=edge[i].u; int v=edge[i].v; int fu=uf.find_root(u); int fv=uf.find_root(v); if(count>=n-1){ break; } if(fu!=fv){ ans+=edge[i].cost; count++; uf.Union(fu,fv); } } printf("%d\n",ans); } return 0; }