ACM Contest and Blackout UVA - 10600 次小生成树之Prim算法

  In order to prepare the “The First National ACM School Contest” (in 20??) the major of the city decided to provide all the schools with a reliable source of power. (The major is really afraid of blackoutsJ). So, in order to do that, power station “Future” and one school (doesn’t matter which one) must be connected; in addition, some schools must be connected as well.

   You may assume that a school has a reliable source of power if it’s connected directly to “Future”, or to any other school that has a reliable source of power. You are given the cost of connection between some schools. The major has decided to pick out two the cheapest connection plans – the cost of the connection is equal to the sum of the connections between the schools. Your task is to help the major — find the cost of the two cheapest connection plans.

Input

   The Input starts with the number of test cases, T (1 < T < 15) on a line. Then T test cases follow. The first line of every test case contains two numbers, which are separated by a space, N (3 < N < 100) the number of schools in the city, and M the number of possible connections among them. Next M lines contain three numbers Ai , Bi , Ci , where Ci is the cost of the connection (1 < Ci < 300) between schools Ai and Bi . The schools are numbered with integers in the range 1 to N.

Output

  For every test case print only one line of output. This line should contain two numbers separated by a single space – the cost of two the cheapest connection plans. Let S1 be the cheapest cost and S2 the next cheapest cost. It’s important, that S1 = S2 if and only if there are two cheapest plans, otherwise S1 < S2. You can assume that it is always possible to find the costs S1 and S2.

Sample Input

2

5 8

1 3 75

3 4 51

2 4 19

3 2 95

2 5 42

5 4 31

1 2 9

3 5 66

9 14

1 2 4

1 8 8

2 8 11

3 2 8

8 9 7

8 7 1

7 9 6

9 3 2

3 4 7

3 6 4

7 6 2

4 6 14

4 5 9

5 6 10

Sample Output

110 121

37 37

 

题意:有n个学校要通过电线相互连接,给你每个学校之间的电线图,请你给出两套花费最少的连接方案,让第一个是最便宜的成本,第二个是较便宜的成本

 

思路:保证连接的最小花费,很明显就是最小生成树,题意要的是两个成本,那么另一个应该是次小生成树了,代码没什么难的,直接套模板就行。

 

代码:

  1 #include <cstdio>
  2 #include <fstream>
  3 #include <algorithm>
  4 #include <cmath>
  5 #include <deque>
  6 #include <vector>
  7 #include <queue>
  8 #include <string>
  9 #include <cstring>
 10 #include <map>
 11 #include <stack>
 12 #include <set>
 13 #include <sstream>
 14 #include <iostream>
 15 #define mod 998244353
 16 #define eps 1e-6
 17 #define ll long long
 18 #define INF 0x3f3f3f3f
 19 using namespace std;
 20 
 21 //ma存放两点之间的初始距离
 22 int ma[105][105];
 23 //low存放到起点的最小距离,
 24 int low[105];
 25 //per存放与当前点连接距离最短的另一个点
 26 int per[105];
 27 //bj用于判断该点时候在生成树中,con用于标记在生成树中的边
 28 bool bj[105],con[105][105];
 29 //maxn存放两点之间的最大值
 30 int maxn[105][105];
 31 int n;
 32 //最小生成树算法,u表示树的起点
 33 int prim(int u)
 34 {
 35     for(int i=1;i<=n;i++)
 36     {  //初始low的距离与per指向的点
 37         low[i]=ma[u][i];
 38         per[i]=u;
 39     }
 40     //初始化标记
 41     memset(maxn,0,sizeof(maxn));
 42     memset(bj,0,sizeof(bj));
 43     memset(con,0,sizeof(con));
 44     //起点是树上的第一个点,标记
 45     bj[u]=1;
 46     //ans保存树的所有权值和
 47     int ans=0;
 48     //遍历其他n-个点
 49     for(int i=1;i<n;i++)
 50     {
 51          //通过遍历找到距离当前起点的最短的边
 52         int mi=INF;
 53         int v=-1;
 54         for(int j=1;j<=n;j++)
 55         {
 56             if(!bj[j]&&low[j]<mi)
 57             {
 58                 mi=low[j];
 59                 v=j;
 60             }
 61         }
 62         //有最短边后
 63         if(v!=-1)
 64         {
 65             //将这个边的两个点标记
 66             con[v][per[v]]=con[per[v]][v]=1;
 67             //累加权值
 68             ans+=mi;
 69             //标记这个点
 70             bj[v]=1;
 71             //记录这个边的值,
 72             maxn[v][per[v]]=maxn[per[v]][v]=mi;
 73              //再遍历一遍,以更新数据
 74             for(int j=1;j<=n;j++)
 75             {
 76                  //j表示的点再树上,且j不是终点
 77                 if(bj[j]&&j!=v)
 78                 {    //更新v与其他在树上的点之间的最大权值
 79                     maxn[j][v]=maxn[v][j]=max(maxn[j][per[v]],maxn[per[v]][v]);
 80                 }
 81                 //j表示的点不在树上,且满足更新需要
 82                 if(!bj[j]&&low[j]>ma[v][j])
 83                 {
 84                     //更新low的距离和per指向的点
 85                     low[j]=ma[v][j];
 86                     per[j]=v;
 87                 }
 88             }
 89         }
 90     }
 91     return ans;
 92 }
 93 
 94 //次小生成树算法,s表示最小生成树的权值和
 95 int secondprim(int s)
 96 {
 97     int ans=INF;
 98     //遍历所有点与其他点
 99     //求最小生成树在外加一条边并且删除环的最大边后的值的最小值
100     for(int i=1;i<n;i++)
101     {
102         for(int j=i+1;j<=n;j++)
103         {
104             //如果这个边不在树上
105             if(!con[i][j])
106             {   //s+ma-maxn表示最小生成树在加上一条外边
107                 //并删除外边所在环差外边外的最大值。就是子生成树的权值和
108                 ans = min(ans,s+ma[i][j]-maxn[i][j]);
109             }
110         }
111     }
112     return ans;
113 }
114 //初始化地图以保证数据的准确性
115 void build(int s)
116 {
117     for(int i=0;i<=s;i++)
118     {
119         for(int j=0;j<=s;j++)
120         {
121             if(i!=j)
122             {
123                 ma[i][j]=INF;
124             }
125             else
126             {
127                 ma[i][j]=0;
128             }
129         }
130     }
131 }
132 int main()
133 {
134     int t;
135     scanf("%d",&t);
136     while(t--)
137     {
138         int m;
139         scanf("%d %d",&n,&m);
140         build(n);
141         int a,b,c;
142         for(int i=0;i<m;i++)
143         {
144             scanf("%d %d %d",&a,&b,&c);
145             ma[a][b]=ma[b][a]=c;
146         }
147         int s1=prim(1);
148         int s2=secondprim(s1);
149         printf("%d %d\n",s1,s2);
150     }
151 }

 

posted @ 2019-11-11 18:10  木子川  阅读(261)  评论(0编辑  收藏  举报