题意:给你n个点,右n-1条边,每个边都有一个权值,让你求出最小生成树
扩展:最小生成树
最小生成树的概念:在一副加权连通图中,最小生成树包含原图中的所有 n 个结点且权值和最小,并且有保持图连通的最少的边(说的就是不会成环)。
解决最小生成树的算法:prime和kruskal算法
prime算法:
- 先任意选择一条边(一般直接选择第一条),连接与其相连权值最小的点,然后两个点成为一个集合体。
- 找这个不在这个集合体里 但是与集合体相连的权值最小的点 与集合体相连,并把该点归入集合体。
- 重复上一条操作,直到集合体归入了所有的点
kruskal算法:
- 先选择一条权值最小的边,把这条边相连的两个点归成一个集合。
- 再找下一个权值最小的边,但是边相连的两个点不能属于同一个集合,把这条边相连的两个点(这里也可以是集合)归成一个集合
- 重复上一条操作,直到最后只有一个集合体且归入所有的点。
两个的区别:
- 集合的个数:prime算法自始自终只有一个集合,而kruskal算法可以有多个集合,所以kruskal算法要用到并查集。
- 选取的方式:prime算法先是任意选取,再根据选取已有的基础上选取权值最小且不在集合的点(取点)。kruskal算法则是每次以权值最小的边来选取,可以有多个集合(取边)。
本题代码:
prime算法:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 30;
const int inf = 0x3f3f3f3f;
int mp[maxn][maxn], vis[maxn], ans[maxn];//采用邻接矩阵存道路关系
int n;
void prime()
{
int sum=0;
for (int i = 1; i <= n; i++)//先选第一个点为集合
{
vis[i] = 0;
ans[i] = mp[1][i];//存与第一个点成的集合到相连的点的权值
}
vis[1]=1;
for (int i = 1; i <= n; i++)
{
int min1 = inf,p=0;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && ans[j] < min1)//找出不属于集合,但是相连权值最小的点
{
min1 = ans[j];
p = j;
}
}
if(min1==inf)break;//如果找不到了,就说明已经找完了,这里找的次数是n,如果没有这个条件很容易把inf算入
//cout<<min1<<endl;
vis[p] = 1;//标记已经加入集合了
sum+=min1;//加入答案中
for (int j = 1; j <= n; j++)//更新一下于集合相连的点的权值
{
if (!vis[j] && mp[p][j] < ans[j])
{
ans[j] = mp[p][j];
}
}
}
cout << sum << endl;
}
int main()
{
while(cin>>n,n)
{
memset(mp, inf, sizeof(mp));
for (int i = 1; i <n; i++)
{
int m;
char s[10];
scanf("%s%d", s, &m);
for(int j=1;j<=m;j++)
{
int k;
char s1[10];
scanf("%s%d", s1, &k);
mp[s[0] - 'A' + 1][s1[0] - 'A' + 1] = mp[s1[0] - 'A' + 1][s[0] - 'A' + 1]= k;
}
}
prime();
}
return 0;
}
kruskal算法:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 35;
const int inf = 0x3f3f3f3f;
int vis[maxn];//并查集数组
int num;
struct node//用该结构存道路关系
{
int a,b;
int val;
}mp[maxn*maxn];
int n;
bool cmp(node a,node b)
{
return a.val<b.val;
}
int find_father(int x)//并查集寻找祖先
{
if(vis[x]==0)
return x;
else
return vis[x]=find_father(vis[x]);
}
void Kruskal()
{
int ans=0,count1=0;
memset(vis,0,sizeof(vis));
sort(mp,mp+num,cmp);//因为kruskal是靠从边的最小依次查找的,所以直接排序方便查找
for(int i=0;i<num;i++)
{
int x=find_father(mp[i].a);
int y=find_father(mp[i].b);
if(x!=y)//如果两个点所属的集合不同就和并
{
vis[x]=y;
ans+=mp[i].val;
count1++;
if(count1==n-1)//当总数满足n-1就可以不用再找了
break;
}
}
if(count1!=n-1)//如果这个图不是连通的
cout<<-1<<endl;
else
cout<<ans<<endl;
}
int main()
{
while(cin>>n,n)
{
num=0;
for (int i = 1; i <n; i++)
{
int m;
char s[10];
scanf("%s%d", s, &m);
for(int j=1;j<=m;j++)
{
int k;
char s1[10];
scanf("%s%d", s1, &k);
mp[num].a=s[0] - 'A' + 1;
mp[num].b=s1[0] - 'A' + 1;
mp[num].val=k;
num++;
//mp[s[0] - 'A' + 1][s1[0] - 'A' + 1] = mp[s1[0] - 'A' + 1][s[0] - 'A' + 1]= k;
}
}
Kruskal();
}
return 0;
}