【最小生成树】畅通工程

畅通工程

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
 

 

Sample Output
3
?
 

 

Source
浙大计算机研究生复试上机考试-2007年
题目大意:
  输入N,M,表示N条边,M个点,点的编号从1~M,下面输入N行,每一行输入a,b,c表示点a和点b的边权值为c,有可能会出现重复的边,求最小生成树的权值和,所以重边的话,取最小的权值即可。
 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 }
View Code

 修改:2015.6.8(Prim(N^3)+ 邻接矩阵)

 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 }
View Code

 修改:2015.6.8(Prim(N*N*M)+ 邻接表)

  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 }
View Code

 修改:2015.6.8(Kruskal(N^3)+ 邻接矩阵)

 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 }
View Code

 修改:2015.6.8(Kruskal(N*N*M)+ 邻接表)

  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 }
View Code

修改:2015.7.28(快排+并查集维护)

 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 }
View Code

更新:2015.7.30(优先队列+并查集维护)

 

 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 }
View Code

 

 

 

PS:代码都还没有优化过,等暑假集训的时候在好好去搞、

 

posted @ 2014-08-22 14:05  Wurq  阅读(178)  评论(0编辑  收藏  举报