畅通工程大集合(最小生成树)
畅通工程
Description
省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。
Output
对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。
Sample Input
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100
Sample Output
3
?
///克鲁斯卡尔算法
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n,m,sum; struct node { int start;///起点 int end;///终点 int power;///权值 } edge[5050]; int pre[5050]; int cmp(node a,node b) { return a.power<b.power;///按权值排序 } int find(int x)///并查集找祖先 { if(x!=pre[x])///递归法 { pre[x]=find(pre[x]); } return pre[x]; /*int a;///循环法 a=x; while(pre[a]!=a) { a=pre[a]; } return a;*/ } void merge(int x,int y,int n) { int fx =find(x); int fy =find(y); if(fx!=fy) { pre[fx]=fy; sum+=edge[n].power; } } int main() { int i,root; while(scanf("%d%d",&m,&n)!=EOF) { if(m==0) { break; } sum=0; for(i=1; i<=m; i++) { scanf("%d%d%d",&edge[i].start,&edge[i].end,&edge[i].power); } for(i=1; i<=m; i++) ///并查集的初始化 { pre[i]=i; } sort(edge+1,edge+m+1,cmp); for(i=1; i<=m; i++) { merge(edge[i].start,edge[i].end,i); } root=0; for(i=1; i<=n; i++)///判断是否产生了生成树 { if(pre[i]==i) { root++; } if(root>1)///如果根节点大于1说明没有产生最小生成树 { break; } } if(root>1) { printf("?\n"); } else { printf("%d\n",sum); } } return 0; }
///普里姆算法
#include<stdio.h> #include<string.h> #define MAX 0x3f3f3f3f using namespace std; int logo[1010];///用0和1来表示是否被选择过 int map1[1010][1010]; int dis[1010];///记录任意一点到这一点的最近的距离 int n,m; int prim() { int i,j,now; int sum=0; for(i=1;i<=n;i++)///初始化 { dis[i]=MAX; logo[i]=0; } for(i=1;i<=n;i++) { dis[i]=map1[1][i]; } dis[1]=0; logo[1]=1; for(i=1;i<n;i++)///循环查找 { now=MAX; int min1=MAX; for(j=1;j<=n;j++) { if(logo[j]==0&&dis[j]<min1) { now=j; min1=dis[j]; } } if(now==MAX)///防止不成图 { break; } logo[now]=1; sum=sum+min1; for(j=1;j<=n;j++)///填入新点后更新最小距离 { if(logo[j]==0&&dis[j]>map1[now][j]) { dis[j]=map1[now][j]; } } } if(i<n) { printf("?\n"); } else { printf("%d\n",sum); } } int main() { while(scanf("%d%d",&m,&n)!=EOF)///n是点数 { if(m==0) { break; } memset(map1,0x3f3f3f3f,sizeof(map1));///map是邻接矩阵储存图的信息 for(int i=0;i<m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(c<map1[a][b])///防止出现重边 { map1[a][b]=map1[b][a]=c; } } prim(); } return 0; }
还是畅通工程
Description
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最小的公路总长度。
Sample Input
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
Sample Output
3
5
Hint
Hint Huge input, scanf is recommended.
///克鲁斯卡尔算法
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n,m,sum; struct node { int start;///起点 int end;///终点 int power;///权值 }edge[5050]; int pre[5050]; int cmp(node a,node b) { return a.power<b.power;///按权值排序 } int find(int x)///并查集找祖先 { if(x!=pre[x])///递归法 { pre[x]=find(pre[x]); } return pre[x]; /*int a;///循环法 a=x; while(pre[a]!=a) { a=pre[a]; } return a;*/ } void merge(int x,int y,int n) { int fx =find(x); int fy =find(y); if(fx!=fy) { pre[fx]=fy; sum+=edge[n].power; } } int main() { int i; while(scanf("%d",&n)!=EOF) { if(n==0) { break; } sum=0; m=n*(n-1)/2;//边数 for(i=1;i<=m;i++) { scanf("%d%d%d",&edge[i].start,&edge[i].end,&edge[i].power); } for(i=1;i<=m;i++)///并查集的初始化 { pre[i]=i;///每一个点的祖先都是自己 } sort(edge+1,edge+m+1,cmp); for(i=1;i<=m;i++) { merge(edge[i].start,edge[i].end,i); } printf("%d\n",sum); } return 0; }
///普里姆算法
#include<stdio.h> #include<string.h> #define MAX 0x3f3f3f3f using namespace std; int logo[1010];///用0和1来表示是否被选择过 int map1[1010][1010]; int dis[1010];///记录任意一点到这一点的最近的距离 int n,m; int prim() { int i,j,now; int sum=0; for(i=1; i<=n; i++) ///初始化 { dis[i]=MAX; logo[i]=0; } for(i=1; i<=n; i++)///任意一个点到第一个点的距离 { dis[i]=map1[1][i]; } dis[1]=0; logo[1]=1;///第一个点已经被访问过,加入可选顶点集 for(i=1; i<n; i++) { now=MAX; int min1=MAX; for(j=1; j<=n; j++)///再找到一条以可选顶点集里为顶点的一条边 { if(logo[j]==0&&dis[j]<min1) { now=j; min1=dis[j]; }///循环查找最小值 } if(now==MAX)///防止不成图 { break; } logo[now]=1; sum=sum+min1; for(j=1; j<=n; j++) ///填入新点后更新最小距离,剩余各点到可选顶点集的距离 { if(logo[j]==0&&dis[j]>map1[now][j]) { dis[j]=map1[now][j]; } } } printf("%d\n",sum); } int main() { while(scanf("%d",&n)!=EOF)///n是点数 { if(n==0) { break; } m=n*(n-1)/2l; memset(map1,0x3f3f3f3f,sizeof(map1));///map是邻接矩阵储存图的信息 for(int i=0; i<m; i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if(c<map1[a][b])///防止出现重边 { map1[a][b]=map1[b][a]=c; } } prim(); } return 0; }
继续畅通工程
Description
省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。
当N为0时输入结束。
当N为0时输入结束。
Output
每个测试用例的输出占一行,输出全省畅通需要的最低成本。
Sample Input
3
1 2 1 0
1 3 2 0
2 3 4 0
3
1 2 1 0
1 3 2 0
2 3 4 1
3
1 2 1 0
1 3 2 1
2 3 4 1
0
Sample Output
3
1
0
///克鲁斯卡尔算法
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n,m,sum; struct node { int start;///起点 int end;///终点 int power;///权值 }edge[5050]; int pre[5050]; int cmp(node a,node b) { return a.power<b.power;///按权值排序 } int find(int x)///并查集找祖先 { if(x!=pre[x])///递归法 { pre[x]=find(pre[x]); } return pre[x]; /*int a;///循环法 a=x; while(pre[a]!=a) { a=pre[a]; } return a;*/ } void merge(int x,int y,int n) { int fx =find(x); int fy =find(y); if(fx!=fy) { pre[fx]=fy; sum+=edge[n].power; } } int main() { int i,flag; while(scanf("%d",&n)!=EOF) { if(n==0) { break; } sum=0; m=n*(n-1)/2; for(i=1;i<=m;i++) { scanf("%d%d%d%d",&edge[i].start,&edge[i].end,&edge[i].power,&flag); if(flag==1) { edge[i].power=0;///已经修好的公路的成本更新为0; } } for(i=1;i<=m;i++)///并查集的初始化 { pre[i]=i; } sort(edge+1,edge+m+1,cmp); for(i=1;i<=m;i++) { merge(edge[i].start,edge[i].end,i); } printf("%d\n",sum); } return 0; }
///普里姆算法
#include<stdio.h> #include<string.h> #define MAX 0x3f3f3f3f using namespace std; int logo[1010];///用0和1来表示是否被选择过 int map1[1010][1010]; int dis[1010];///记录任意一点到这一点的最近的距离 int n,m; int prim() { int i,j,now; int sum=0; for(i=1; i<=n; i++) ///初始化 { dis[i]=MAX; logo[i]=0; } for(i=1; i<=n; i++)///任意一个点到第一个点的距离 { dis[i]=map1[1][i]; } dis[1]=0; logo[1]=1;///第一个点已经被访问过,加入可选顶点集 for(i=1; i<n; i++) { now=MAX; int min1=MAX; for(j=1; j<=n; j++)///再找到一条以可选顶点集里为顶点的一条边 { if(logo[j]==0&&dis[j]<min1) { now=j; min1=dis[j]; }///循环查找最小值 } if(now==MAX)///防止不成图 { break; } logo[now]=1; sum=sum+min1; for(j=1; j<=n; j++) ///填入新点后更新最小距离,剩余各点到可选顶点集的距离 { if(logo[j]==0&&dis[j]>map1[now][j]) { dis[j]=map1[now][j]; } } } printf("%d\n",sum); } int main() { int flag; while(scanf("%d",&n)!=EOF)///n是点数 { if(n==0) { break; } m=n*(n-1)/2l; memset(map1,0x3f3f3f3f,sizeof(map1));///map是邻接矩阵储存图的信息 for(int i=0; i<m; i++) { int a,b,c; scanf("%d%d%d%d",&a,&b,&c,&flag); if(!flag) { map1[a][b]=map1[b][a]=c; } else { map1[b][a]=map1[a][b]=0;///已将连接的两个点之间的距离换成0 } } prim(); } return 0; }