kruskal算法可以解决最小最大生成树问题,要一个info结构体来储存两个相互连接的电以及它们之间的权值,以及需要并查集的知识来完成边的选取。
并查集需要一个数组来储存需要的信息,pre数组,pre[i]表示结点的祖先结点,pre[i]初始化为i。
并查集需要两个函数来完成相应的操作,Find(int a)和Union(int a, int b) Find函数是用来查找a的祖先,Union函数是用来合并两个结点
kruskal算法思路如下:
1.对于所有的边,按照边的权值的大小从小到大排序
2.将排序好的结构体从小到大遍历,对于一条边上的两个点判断这两个点是否在同一颗树里,如果是就跳过,如果不是就合并这两个点。
3.重复2直到选了n-1条边(n为点的个数)如果选不到n条边说明该图不是连通图
来个例子
权值从小到大排序 3 5 6 7 8 9 10 12 14 15
点 1 3 3 5 3 5 1 4 3 6
点 2 5 4 6 1 7 6 5 2 8
1.点1和点2不在同一颗树里,合并点1点2
2.点3和点5不在同一颗树里,合并点3点5
3.点3和点4不在同一颗树里,合并点3点4
4.点5和点6不在同一颗树里,合并点5点6
5.点1和点3不在同一颗树里,合并点1点3
6.点5和点7不在同一颗树里,合并点5点7
7.点1和点6在同一颗树里,不合并
8.点4和点5不在同一颗树里,不合并
9.点3和点2在同一颗树里,不合并
10.点6和点8不在同一颗树里,合并点6点8
已经选了七条边,结束算法
代码
洛谷P1194 买礼物
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
struct info{
int s,e,w;
bool operator < (info & a) const
{
return w < a.w;
}
}node[250010];
int pre[510],v,n,c;
int Find(int a)
{
if(pre[a] == a) return a;
else return pre[a] = Find(pre[a]);
}
void Union(int a,int b)
{
int _a = Find(a),_b = Find(b);
if(a == b) return;
else{
pre[_a] = _b;
}
}
void init()
{
for(int i = 1;i < 510; i++)
pre[i] = i;
}
void kruskal(int cnt)
{
int t = 0,sum = 0;
for(int i = 0;i < cnt; i++){
if(Find(node[i].s) != Find(node[i].e)){
t++;
Union(node[i].s,node[i].e);
sum += node[i].w;
}
if(t == n-1) break;
}
cout << sum + v;
}
int main()
{
int cnt = 0;
cin >> v >> n;
for(int i = 1;i <= n; i++)
for(int j = 1;j <=n; j++){
cin >> c;
if(i == j) c = INF;
else if(c == 0 || c > v) c = v;
node[cnt].s = i;node[cnt].e = j;node[cnt].w = c;
cnt++;
}
sort(node,node+cnt-1);
init();
kruskal(cnt);
return 0;
}