【最小生成树】畅通工程
畅通工程
Time Limit : 1000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 20 Accepted Submission(s) : 14
Problem Description
省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N 行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从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
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100
Sample Output
3
?
Source
浙大计算机研究生复试上机考试-2007年
题目大意:
输入N,M,表示N条边,M个点,点的编号从1~M,下面输入N行,每一行输入a,b,c表示点a和点b的边权值为c,有可能会出现重复的边,求最小生成树的权值和,所以重边的话,取最小的权值即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #define MAXN 205 5 #define inf 1000000000 6 int mat[205][205]; 7 int prim(int n,int* pre){ 8 int min[MAXN],ret=0; 9 int v[MAXN],i,j,k; 10 for (i=1;i<=n;i++) 11 min[i]=inf,v[i]=0,pre[i]=-1; 12 for (min[j=1]=0;j<=n;j++) 13 { 14 for (k=-1,i=1;i<=n;i++) 15 if (!v[i]&&(k==-1||min[i]<min[k])) 16 k=i; 17 for (v[k]=1,ret+=min[k],i=1;i<=n;i++) 18 if (!v[i]&&mat[k][i]<min[i]) 19 min[i]=mat[pre[i]=k][i]; 20 } 21 return ret; 22 } 23 24 int main() 25 { 26 int T,N,i,j,a,b,c,pre[205],sign; 27 while(scanf("%d%d",&T,&N)!=EOF&&T) 28 { 29 sign=0; 30 for(i=1;i<=N;i++) 31 for(j=1;j<=N;j++) 32 { 33 if(i==j) 34 mat[i][j]=0; 35 mat[i][j]=inf; 36 } 37 memset(pre,0,sizeof(pre)); 38 for(i=1;i<=T;i++) 39 { 40 scanf("%d%d%d",&a,&b,&c); 41 if(mat[a][b]==inf)sign++; 42 if(mat[a][b]>c) 43 { 44 mat[a][b]=c; 45 mat[b][a]=c; 46 } 47 } 48 if(sign>=N-1) 49 printf("%d\n",prim(N,pre)); 50 else 51 printf("?\n"); 52 } 53 return 0; 54 }
修改:2015.6.8(Prim(N^3)+ 邻接矩阵)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX 1100 5 using namespace std; 6 int Map[MAX][MAX];/*邻接矩阵*/ 7 int Point[MAX];/*标记点i的状态*/ 8 void Cread(int N) 9 { 10 for(int i=0;i<=N;i++) 11 { 12 Point[i]=1;/*标记1,表示可用*/ 13 for(int j=0;j<=N;j++) 14 Map[i][j]=- 1;/*初始化为-1,表示为空*/ 15 } 16 } 17 int Prim(int N)/*如果可构成最小生成树,返回最小权值*/ 18 { /*否则返回-1,表示无法构成最小生成树,O(N^3)*/ 19 int i,j,k,ii,jj; 20 int Sum_Min=0,MIN; 21 int P[MAX]={0};/*记录点集合*/ 22 P[1]=1;Point[1]=0; 23 for(k=2;k<=N;k++) 24 { 25 for(i=1,MIN=-1;i<k;i++) 26 { 27 for(j=1;j<=N;j++) 28 { 29 if(Map[P[i]][j]==-1)continue; 30 if(Point[j]) 31 if(MIN==-1||MIN>Map[P[i]][j]) 32 { 33 ii=P[i];jj=j; 34 MIN=Map[P[i]][j]; 35 P[k]=j; 36 } 37 } 38 } 39 if(MIN==-1)break; 40 Sum_Min+=MIN; 41 Map[ii][jj]=-1; 42 Map[jj][ii]=-1; 43 Point[P[k]]=0; 44 } 45 if(k==N+1)return Sum_Min; 46 else return -1; 47 } 48 int main() 49 { 50 int T,i,j,k,N,M; 51 while(scanf("%d%d",&M,&N)!=EOF) 52 { 53 if(M==0)break; 54 Cread(N); 55 int a,b,c; 56 for(i=0;i<M;i++) 57 { 58 scanf("%d%d%d",&a,&b,&c); 59 if(Map[a][b]==-1||Map[a][b]>c) 60 { 61 Map[a][b]=c; 62 Map[b][a]=c; 63 } 64 } 65 int Min=Prim(N); 66 if(Min==-1)printf("?\n"); 67 else printf("%d\n",Min); 68 69 } 70 return 0; 71 }
修改:2015.6.8(Prim(N*N*M)+ 邻接表)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <map> 5 #include <stack> 6 #include <queue> 7 #include <algorithm> 8 #include <math.h> 9 #define MAX 110 10 using namespace std; 11 int Point[MAX];/*标记点i的状态*/ 12 int First[MAX]; /*First[x]:x表示头结点为x,First[x]表示下一条边的编号*/ 13 struct edge 14 { 15 int TO; /*下一个顶点*/ 16 int Next; /*记录下一条边的编号*/ 17 int Vlaue; /*权值*/ 18 }ID[3*MAX]; /*边表,无向图的边数记得多弄些*/ 19 int SIGN;/*链表的边数,链表的边数=无向图边数*2=有向图边数,初始化为1*/ 20 void Add_E(int x,int y,int z) /*添加边*/ 21 { 22 ID[SIGN].TO=y; 23 ID[SIGN].Vlaue=z; 24 ID[SIGN].Next=First[x]; 25 First[x]=SIGN++; 26 } 27 28 void Jude(int x,int y,int c)/*判断该边是否已经存在,未存在新增边*/ 29 { /*如果已经存在,则先找到该条边,重新判断其权值的大小*/ 30 int i; 31 int TMD=1;/*TMD=1,需要新加边,TMD=2,表示更新边权值,TMD=0,不需要操作*/ 32 for(i=First[x];i!=0;i=ID[i].Next) //查找与该点相关的点 33 { 34 if(ID[i].TO==y)/*如果能够找到该边*/ 35 { 36 TMD=0; 37 if(ID[i].Vlaue>c)/*取权值最小的值*/ 38 { 39 ID[i].Vlaue=c; 40 TMD=2;break; 41 } 42 } 43 } 44 if(TMD==2) 45 { 46 for(i=First[y];i!=0;i=ID[i].Next) //查找与该点相关的点 47 { 48 if(ID[i].TO==x)/*如果能够找到该边*/ 49 { 50 if(ID[i].Vlaue>c)/*取权值最小的值*/ 51 { 52 ID[i].Vlaue=c; 53 break; 54 } 55 } 56 } 57 return ; 58 } 59 if(TMD==1) 60 { 61 Add_E(x,y,c);/*无线图,所以需要左右两边*/ 62 Add_E(y,x,c);/*无线图,所以需要左右两边*/ 63 } 64 return ; 65 } 66 67 void Cread(int N) 68 { 69 for(int i=0;i<=N;i++) 70 { 71 Point[i]=1;/*标记1,表示可用*/ 72 First[i]=0; 73 } 74 } 75 int Prim(int N)/*如果可构成最小生成树,返回最小权值*/ 76 { /*否则返回-1,表示无法构成最小生成树,O(N*N*M)*/ 77 int i,j,k; 78 int Sum_Min=0,MIN; 79 int P[MAX]={0};/*记录点集合*/ 80 P[1]=1;Point[1]=0; 81 for(k=2;k<=N;k++) 82 { 83 for(i=1,MIN=-1;i<k;i++) 84 { 85 for(j=First[P[i]];j!=0;j=ID[j].Next) //查找与该点相关的点 86 { 87 if(Point[ID[j].TO]) 88 if(MIN==-1||MIN>ID[j].Vlaue) 89 { 90 MIN=ID[j].Vlaue; 91 P[k]=ID[j].TO; 92 } 93 } 94 } 95 if(MIN==-1)break; 96 Sum_Min+=MIN; 97 Point[P[k]]=0; 98 } 99 if(k==N+1)return Sum_Min; 100 else return -1; 101 } 102 int main() 103 { 104 int T,i,j,k,N,M,TMD; 105 while(scanf("%d%d",&M,&N)!=EOF) 106 { 107 if(M==0)break; 108 Cread(N);SIGN=1; 109 int a,b,c; 110 for(i=0;i<M;i++) 111 { 112 scanf("%d%d%d",&a,&b,&c); 113 Jude(a,b,c); 114 } 115 int Min=Prim(N); 116 if(Min==-1)printf("?\n"); 117 else printf("%d\n",Min); 118 } 119 return 0; 120 }
修改:2015.6.8(Kruskal(N^3)+ 邻接矩阵)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX 1100 5 using namespace std; 6 int ID[MAX];/*ID[i]=i表示i为独立点*/ 7 int Map[MAX][MAX];/*邻接矩阵*/ 8 int Point[MAX];/*标记点i的状态*/ 9 int Point_Num; 10 void Cread(int N) 11 { 12 Point_Num=N; 13 for(int i=0;i<=N;i++) 14 { 15 Point[i]=1;/*标记1,表示可用*/ 16 ID[i]=i; 17 for(int j=0;j<=N;j++) 18 Map[i][j]=- 1;/*初始化为-1,表示为空*/ 19 } 20 } 21 int Find(int x)/*寻找父亲节点,递归形式,有时需要栈扩展*/ 22 { 23 int tmp; 24 if(ID[x]!=x)tmp=Find(ID[x]); 25 else return x; 26 ID[x]=tmp; /*路径压缩*/ 27 return tmp; 28 } 29 30 int Add(int x,int y)/*添加点操作*/ 31 { 32 x=Find(x); 33 y=Find(y); 34 if(x!=y) 35 { 36 ID[x]=y; 37 return 1; 38 } 39 else return 0; 40 } 41 int Kruskal(int N)/*如果可构成最小生成树,返回最小权值*/ 42 { /*否则返回-1,表示无法构成最小生成树,O(N^3)*/ 43 int i,j,k,ii,jj; 44 int Sum_Min=0,MIN; 45 for(k=2;k<=N;k++) 46 { 47 for(i=1,MIN=-1;i<=N;i++) 48 { 49 for(j=1;j<=N;j++) 50 { 51 if(Map[i][j]==-1)continue; 52 if(MIN==-1||MIN>Map[i][j]) 53 { 54 ii=i;jj=j; 55 MIN=Map[i][j]; 56 } 57 } 58 } 59 if(MIN==-1)break; 60 if(Add(ii,jj)) 61 { 62 Sum_Min+=MIN; 63 Point_Num--; 64 } 65 else k--; 66 Map[ii][jj]=-1; 67 Map[jj][ii]=-1; 68 } 69 if(Point_Num==1)return Sum_Min; 70 else return -1; 71 } 72 int main() 73 { 74 int T,i,j,k,N,M; 75 while(scanf("%d%d",&M,&N)!=EOF) 76 { 77 if(M==0)break; 78 Cread(N); 79 int a,b,c; 80 for(i=0;i<M;i++) 81 { 82 scanf("%d%d%d",&a,&b,&c); 83 if(Map[a][b]==-1||Map[a][b]>c) 84 { 85 Map[a][b]=c; 86 Map[b][a]=c; 87 } 88 } 89 int Min=Kruskal(N); 90 if(Min==-1)printf("?\n"); 91 else printf("%d\n",Min); 92 } 93 return 0; 94 }
修改:2015.6.8(Kruskal(N*N*M)+ 邻接表)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #include <map> 5 #include <stack> 6 #include <queue> 7 #include <algorithm> 8 #include <math.h> 9 #define MAX 110 10 using namespace std; 11 int ID_S[MAX];/*ID_S[i]=i表示i为独立点*/ 12 int First[MAX]; /*First[x]:x表示头结点为x,First[x]表示下一条边的编号*/ 13 int Point_Num; 14 struct edge 15 { 16 int TO; /*下一个顶点*/ 17 int Next; /*记录下一条边的编号*/ 18 int Vlaue; /*权值*/ 19 }ID[3*MAX]; /*边表,无向图的边数记得多弄些*/ 20 int SIGN;/*链表的边数,链表的边数=无向图边数*2=有向图边数,初始化为1*/ 21 void Add_E(int x,int y,int z) /*添加边*/ 22 { 23 ID[SIGN].TO=y; 24 ID[SIGN].Vlaue=z; 25 ID[SIGN].Next=First[x]; 26 First[x]=SIGN++; 27 } 28 29 void Jude(int x,int y,int c)/*判断该边是否已经存在,未存在新增边*/ 30 { /*如果已经存在,则先找到该条边,重新判断其权值的大小*/ 31 int i; 32 int TMD=1;/*TMD=1,需要新加边,TMD=2,表示更新边权值,TMD=0,不需要操作*/ 33 for(i=First[x];i!=0;i=ID[i].Next) //查找与该点相关的点 34 { 35 if(ID[i].TO==y)/*如果能够找到该边*/ 36 { 37 TMD=0; 38 if(ID[i].Vlaue>c)/*取权值最小的值*/ 39 { 40 ID[i].Vlaue=c; 41 TMD=2;break; 42 } 43 } 44 } 45 if(TMD==2) 46 { 47 for(i=First[y];i!=0;i=ID[i].Next) //查找与该点相关的点 48 { 49 if(ID[i].TO==x)/*如果能够找到该边*/ 50 { 51 if(ID[i].Vlaue>c)/*取权值最小的值*/ 52 { 53 ID[i].Vlaue=c; 54 break; 55 } 56 } 57 } 58 return ; 59 } 60 if(TMD==1) 61 { 62 Add_E(x,y,c);/*无线图,所以需要左右两边*/ 63 Add_E(y,x,c);/*无线图,所以需要左右两边*/ 64 } 65 return ; 66 } 67 68 void Dele_x_y(int x,int y)/*删除x-y的边*/ 69 { 70 int i,j;/*起始点要注意*/ 71 for(i=j=First[x];i!=0;j=i,i=ID[i].Next) 72 { 73 if(ID[i].TO==y)/*如果能够找到该边*/ 74 { 75 if(i==j)First[x]=ID[i].Next; 76 ID[j].Next=ID[i].Next; 77 break; 78 } 79 } 80 for(i=j=First[y];i!=0;j=i,i=ID[i].Next) 81 { 82 if(ID[i].TO==x)/*如果能够找到该边*/ 83 { 84 if(i==j)First[y]=ID[i].Next; 85 ID[j].Next=ID[i].Next; 86 break; 87 } 88 } 89 return ; 90 } 91 int Find(int x)/*寻找父亲节点,递归形式,有时需要栈扩展*/ 92 { 93 int tmp; 94 if(ID_S[x]!=x)tmp=Find(ID_S[x]); 95 else return x; 96 ID_S[x]=tmp; /*路径压缩*/ 97 return tmp; 98 } 99 100 int Add(int x,int y)/*添加点操作*/ 101 { 102 x=Find(x); 103 y=Find(y); 104 if(x!=y) 105 { 106 ID_S[x]=y; 107 return 1; 108 } 109 return 0; 110 } 111 void Cread(int N) 112 { 113 Point_Num=N; 114 for(int i=0;i<=N;i++) 115 { 116 First[i]=0; 117 ID_S[i]=i; 118 } 119 } 120 121 122 int Kruskal(int N)/*如果可构成最小生成树,返回最小权值*/ 123 { /*否则返回-1,表示无法构成最小生成树,O(N*N*M)*/ 124 int i,j,k,ii,jj; 125 int Sum_Min=0,MIN; 126 for(k=2;k<=N;k++) 127 { 128 for(i=1,MIN=-1;i<=N;i++) 129 { 130 for(j=First[i];j!=0;j=ID[j].Next) //查找与该点相关的点 131 { 132 if(MIN==-1||MIN>ID[j].Vlaue) 133 { 134 ii=i; 135 jj=ID[j].TO; 136 MIN=ID[j].Vlaue; 137 } 138 } 139 } 140 if(MIN==-1)break; 141 if(Add(ii,jj))/*判断是否同一棵树*/ 142 { 143 Sum_Min+=MIN;/*是的话才相加*/ 144 Point_Num--; 145 } 146 else k--; /*不是的话k--*/ 147 Dele_x_y(ii,jj);/*删除该边*/ 148 } 149 if(Point_Num==1)return Sum_Min; 150 else return -1; 151 } 152 int main() 153 { 154 int T,i,j,k,N,M,TMD; 155 while(scanf("%d%d",&M,&N)!=EOF) 156 { 157 if(M==0)break; 158 Cread(N);SIGN=1; 159 int a,b,c; 160 for(i=0;i<M;i++) 161 { 162 scanf("%d%d%d",&a,&b,&c); 163 Jude(a,b,c); 164 } 165 int Min=Kruskal(N); 166 if(Min==-1)printf("?\n"); 167 else printf("%d\n",Min); 168 } 169 return 0; 170 }
修改:2015.7.28(快排+并查集维护)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <math.h> 4 #include <algorithm> 5 #define MAX 100100 6 using namespace std; 7 int ID[MAX];/*MAX为点数目*/ 8 int Sign;/*记录边数*/ 9 struct Poin{int a,b,c;}Edge[MAX*3]; 10 int cmp(Poin a,Poin b) 11 { 12 return a.c<b.c; 13 } 14 15 void Cread(int N)/*初始化*/ 16 { 17 int i,j; 18 Sign=0; 19 for(i=0;i<=N;i++)ID[i]=i; 20 } 21 22 int Find(int x)/*递归的路径压缩*/ 23 { 24 if(x!=ID[x])ID[x]=Find(ID[x]); 25 return ID[x]; 26 } 27 28 int Add(int a,int b)/*添加点*/ 29 { 30 int A=Find(a); 31 int B=Find(b); 32 if(A!=B){ID[A]=B;Sign++;return 1;} 33 else return 0; 34 } 35 int Quick_Edge(int M)/*快排+并查集维护*/ 36 { 37 int k,Sum=0; 38 sort(Edge,Edge+M,cmp); 39 for(k=0;k<M;k++) 40 { 41 if(Add(Edge[k].a,Edge[k].b)) 42 { 43 Sum+=Edge[k].c; 44 } 45 } 46 return Sum; 47 } 48 int main() 49 { 50 int N,M,i,k,A,B,C,Sum; 51 while(scanf("%d%d",&M,&N)!=EOF) 52 { 53 if(M==0)break; 54 Cread(N); 55 for(i=0;i<M;i++) 56 { 57 scanf("%d%d%d",&A,&B,&C); 58 Edge[i].a=A; 59 Edge[i].b=B; 60 Edge[i].c=C; 61 } 62 Sum=Quick_Edge(M); 63 if(Sign==N-1) printf("%d\n",Sum); 64 else printf("?\n"); 65 } 66 return 0; 67 }
更新:2015.7.30(优先队列+并查集维护)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <queue> 4 using namespace std; 5 #define MAX 1010 6 int ID[MAX]; 7 int SIGN; 8 struct Node 9 { 10 int x,y,v; 11 friend bool operator<(Node a,Node b) 12 { 13 return a.v>b.v; 14 } 15 }; 16 void Cread(int N)/*初始化*/ 17 { 18 for(int i=0;i<=N;i++)ID[i]=i; 19 } 20 int Find(int x)/*路径压缩*/ 21 { 22 int TMD=x,TMP; 23 while(TMD!=ID[TMD])TMD=ID[TMD]; 24 while(x!=TMD){TMP=ID[x];ID[x]=TMD;x=TMP;} 25 return x; 26 } 27 int Add(int a,int b)/*添加点*/ 28 { 29 int A=Find(a); 30 int B=Find(b); 31 if(A!=B){ID[A]=B;SIGN++;return 1;} 32 else return 0; 33 } 34 int Pri_Queue_ID(priority_queue <Node>ID_S) 35 { /*优先队列+并查集维护*/ 36 int SUM=0; 37 Node NUM; 38 while(!ID_S.empty()) 39 { 40 NUM=ID_S.top();ID_S.pop(); 41 if(Add(NUM.x,NUM.y)) 42 { 43 SUM+=NUM.v; 44 } 45 } 46 return SUM; 47 } 48 int main() 49 { 50 int N,M,i,j,a,b,A,B,SUM; 51 Node NUM; 52 while(scanf("%d%d",&M,&N)&&M) 53 { 54 priority_queue <Node>ID_S; 55 Cread(N);SIGN=0;SUM=0; 56 for(i=0;i<M;i++) 57 { 58 scanf("%d%d%d",&NUM.x,&NUM.y,&NUM.v); 59 ID_S.push(NUM); 60 } 61 SUM=Pri_Queue_ID(ID_S); 62 if(N-1==SIGN)printf("%d\n",SUM); 63 else printf("?\n"); 64 } 65 return 0; 66 }
PS:代码都还没有优化过,等暑假集训的时候在好好去搞、
转载请备注:
**************************************
* 作者: Wurq
* 博客: https://www.cnblogs.com/Wurq/
* Gitee: https://gitee.com/wurq
**************************************
**************************************
* 作者: Wurq
* 博客: https://www.cnblogs.com/Wurq/
* Gitee: https://gitee.com/wurq
**************************************