思路分析
本题是一道经典的图最小生成树模板题,我分别利用邻接矩阵、邻接表存储图结合prime算法,以及存储边结合kruskal算法,求解了本题。之所以这么麻烦地都写出来,就是为了整理一下这个知识点,下次再看的时候方便一些吧。
方法1:基于邻接矩阵的 prime 算法
方法2:基于邻接表的 prime 算法
玛の。。。。看到这个就生气,调了一晚上都没搞好。。最后是因为双向图,我在vector存储时只存储了单方向的!!!!
方法3:并查集 + kruskal 算法
代码——方法1
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int INF = 0x3fffffff;
int G[30][30];
int d[30];
bool vis[30] = {false};
void init()
{
fill(G[0], G[0]+30*30, INF);
fill(d, d+30, INF);
fill(vis, vis+30, false);
}
int prime(int n) // 默认0号结点为初始结点,函数返回最小生成树的边权和
{
d[0] = 0; // 默认从0开始,也可以换成其他结点
int ans = 0; // 存放边权和
for(int i=0; i<n; i++)
{
int u = -1;
int min = INF;
for(int j=0; j<n; j++)
{
if(vis[j]==false && d[j]<min)
{
u = j;
min = d[j];
}
}
if(u == -1)
{
return -1;
}
vis[u] = true;
ans += d[u];
for(int v=0; v<n; v++)
{
if(vis[v]==false && G[u][v]!=INF && G[u][v]<d[v])
{
d[v] = G[u][v];
}
}
}
return ans;
}
int main()
{
// freopen("input.txt", "r", stdin);
int n;
while(scanf("%d", &n) != EOF)
{
if(n != 0)
{
init(); // 初始化
for(int i=0; i<n-1; i++)
{
char start;
int num;
getchar();
scanf("%c %d", &start, &num);
if(num != 0)
{
for(int j=0; j<num; j++)
{
char temp;
int weight;
getchar();
scanf("%c %d", &temp, &weight);
G[start-'A'][temp-'A'] = G[temp-'A'][start-'A'] = weight;
}
}
}
int ans = prime(n);
printf("%d\n", ans);
}
}
// fclose(stdin);
return 0;
}
代码——方法2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
struct node
{
int v; // 结点编号
int weight; // 边的权重
};
const int INF = 0x3fffffff;
vector<node> Adj[30]; // 邻接表
int d[30];
bool vis[30] = {false};
void init()
{
fill(d, d+30, INF);
fill(vis, vis+30, false);
for(int i=0; i<30; i++)
{
Adj[i].clear(); // 清空vector
}
}
int prime(int n)
{
d[0] = 0;
int ans = 0;
for(int i=0; i<n; i++)
{
int u = -1;
int min = INF;
for(int j=0; j<n; j++)
{
if(vis[j]==false && d[j]<min)
{
u = j;
min = d[j];
}
}
if(u == -1)
{
return -1;
}
vis[u] = true;
ans += d[u];
for(int j=0; j<Adj[u].size(); j++) // 更新和u直接相邻的未访问过的结点
{
int v = Adj[u][j].v; // u能直接到达的结点v
if(vis[v]==false && Adj[u][j].weight<d[v])
{
d[v] = Adj[u][j].weight;
}
}
}
return ans;
}
int main()
{
// freopen("input.txt", "r", stdin);
int n;
while(scanf("%d", &n) != EOF)
{
if(n != 0)
{
init(); // 初始化
for(int i=0; i<n-1; i++)
{
char start;
int num;
getchar();
scanf("%c %d", &start, &num);
if(num != 0)
{
for(int j=0; j<num; j++)
{
node dot1, dot2;
char temp;
int weight;
getchar();
scanf("%c %d", &temp, &weight);
dot1.v = temp - 'A';
dot1.weight = weight;
dot2.v = start - 'A';
dot2.weight = weight;
Adj[start-'A'].push_back(dot1);
Adj[temp-'A'].push_back(dot2); // 无向图!!!!!!
}
}
}
int ans = prime(n);
printf("%d\n", ans);
}
}
// fclose(stdin);
return 0;
}
代码——方法3
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 30; // 结点的最大数量
const int maxr = 105; // 边的最大数量
struct edge
{
int u, v; // 边的两个端点的编号
int cost; // 边权
}E[maxr];
int father[maxn]; // 并查集使用的父亲结点
bool cmp(edge a, edge b)
{
return a.cost < b.cost;
}
int findFather(int x)
{
int a = x;
while(x != father[x])
{
x = father[x];
}
while(a != father[a])
{
int z = a;
a = father[a]; // 更新a
father[z] = x; // 将所有结点的父节点改为x
}
return x;
}
int kruskal(int n, int m) // n为结点数,m为边数
{
int ans = 0; // 边长和
int num_edge = 0; // 选取边的个数
for(int i=0; i<maxn; i++) // 初始化并查集
{
father[i] = i;
}
sort(E, E+m, cmp); // 将边长从小到大排序
for(int i=0; i<m; i++)
{
int father_u = findFather(E[i].u);
int father_v = findFather(E[i].v);
if(father_u != father_v)
{
father[father_v] = father_u; // 合并并查集
ans += E[i].cost;
num_edge++;
if(num_edge == n-1) // 记录的边==结点数-1
{
break;
}
}
}
if(num_edge != n-1)
{
return -1; // 图不连通
}
else
{
return ans; // 返回最小生成树的边权和
}
}
int main()
{
// freopen("input.txt", "r", stdin);
int n;
while(scanf("%d", &n)!=EOF && n!=0)
{
int k = 0; // 边的计数器
for(int i=0; i<n-1; i++)
{
char start;
int num;
getchar();
scanf("%c %d", &start, &num);
//sum += num;
if(num != 0)
{
for(int j=0; j<num; j++)
{
char temp;
int weight;
getchar();
scanf("%c %d", &temp, &weight);
E[k].u = start - 'A';
E[k].v = temp - 'A';
E[k++].cost = weight;
}
}
}
int ans = 0;
ans = kruskal(n, k);
printf("%d\n", ans);
}
// fclose(stdin);
return 0;
}