题目描述
有N个村庄,从1到N,你应该修建一些道路,这样每两个村庄就可以连接起来。我们说两个村庄A和B相连,当且仅当A和B之间有一条路,或者存在一个村庄C使得A和C之间有一条路,并且C和B相连。我们知道一些村庄之间已经有一些道路了,你的工作是修建一些道路,这样所有的村庄都连接起来,所有道路的长度都是最小的。
输入格式
第一行是整数N (3 <= N <= 100),即村庄数。然后是N行,第i行包含N个整数,第j个数是村庄i和村庄j之间的距离(距离应该是[1,1000]内的整数)。然后输入整数Q (0 <= Q <= N * (N + 1) / 2),然后是Q行,每一行包含两个整数a和b (1 <= a < b <= N),这意味着a村和b村之间的道路已经建成。
输出格式
输出一个整数,该整数是所有要修建的道路的长度,以便所有村庄都连接起来,并且该值是最小的。
Sample Input
3
0 990 692
990 0 179
692 179 0
1
1 2
Sample Output
179
题目分析
这道题是最小生成树模板题,这里采用prim算法。需要注意的是,题目中已经给出了一些道路的连接,我们在初始化每个点之间的map时,需要将已经连接的道路之间的map设为0。这样就可以直接套用prim模板啦!
Code:
#include<iostream>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 1000
int map[maxn][maxn];
int d[maxn];
int visit[maxn];
int sum;
int minn; int temp;
int n, m; //n个顶点,m条边
int prim()
{
visit[1] = false; //visit[i]=1表示可以访问该点,visit[i]=0表示不可以访问该点
d[1] = 0;
for (int i = 1; i < n; i++) //循环n-1次,找到n-1条边
{
minn = INF;
temp = -1;
for (int j = 1; j <=n; j++) //循环n次,找到未加入最小生成树的最小权值的边和点
{
if (visit[j] && d[j] < minn)
{
minn = d[j];
temp = j;
}
}
if (temp == -1)
{
sum == INF;
return 0;
}
visit[temp] = 0;
sum += minn; //更新最小生成树的权值
for (int k = 1; k <= n; k++) //更新未加入最小生成树的点和边
{
if (visit[k] && d[k] > map[k][temp])
{
d[k] = map[k][temp];
}
}
}
return 0;
}
int main()
{
cin >> n;
memset(map, INF, sizeof(map)); //初始化每两点间的权值为无穷
memset(d, INF, sizeof(d)); //初始化每个点到源点的距离为无穷
memset(visit, 1, sizeof(visit)); //初始化每个点均可访问
sum = 0;
int w;//边(x,y)权值为w
for (int i = 1; i <= n; i++) //存图
{
for (int j = 1; j <= n; j++)
{
cin >> w;
map[i][j] = w;
}
}
int q; int x, y;
cin >> q;
for (int i = 1; i <= q; i++)
{
cin >> x >> y;
map[x][y] = map[y][x] = 0;
}
for (int i = 1; i <= n; i++) //初始化每个点到源点的距离(1是源点)
{
d[i] = map[i][1];
}
prim();
if (sum == INF)
{
cout << "No" << endl; //该图不连通,不能生成最小生成树
}
else
{
cout << sum << endl;
}
return 0;
}