最小生成树 边权非负无向图
prim 算法
使用于领接矩阵版本
与dijkstra极其相似 只是更新的矩阵的数量不同
krukal算法
并查集
从小到大排序
一条边没有联通 就选择这条边
否则 如果联通就不选择这条边
求最大的边权最小
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
const int N = 16005;
struct edge{
int a,b,w;
bool operator<(edge &a){
return w<a.w;
}
}e[N];
int st[N];
int p[N];
int find(int x){
if(x!=p[x]){
p[x]=find(p[x]);
}
return p[x];
}
int main()
{
cin >> n>>m;
for (int i = 1; i <= n; i ++ ) p[i]=i;
for (int i = 1; i <= m; i ++ ){
cin >> e[i].a>>e[i].b>>e[i].w;
}
int res=0;
sort(e+1,e+1+m);
for (int i = 1; i <= m; i ++ ){
int a=find(e[i].a),b=find(e[i].b);
if(a!=b){
p[a]=b;
res=e[i].w;
}
}
cout<<n-1<<" " << res;
return 0;
}
必连边 缩点
再枚举非必边 https://www.acwing.com/activity/content/problem/content/1514/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
const int N = 10005;
int p[N];
struct edge{
int a,b,w;
bool operator<(edge &a ){
return w<a.w;
}
}e[N];
int find(int x){
if(x!=p[x]){
p[x]=find(p[x]);
}
return p[x];
}
int main()
{
cin >> n>>m;
int res=0,k=0;
for (int i = 1; i <= n; i ++ ) p[i]=i;
for (int i = 1; i <= m; i ++ ){
int t,a,b,c;
cin >> t>>a>>b>>c;
if(t==1){
p[find(a)]=p[find(b)];
res+=c;
}
else{
e[k++]={a,b,c};
}
}
sort(e,e+k);
for (int i = 0; i < k; i ++ ){
int a=find(e[i].a),b=find(e[i].b);
if(a!=b){
p[a]=b;
res+=e[i].w;
}
}
cout << res;
return 0;
}
横竖的连线价值不一样 优先连接价值小的
https://www.acwing.com/problem/content/1146/
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010, M = N * N, K = 2 * N * N;
int n, m, k;
int ids[N][N];
struct Edge
{
int a, b, w;
}e[K];
int p[M];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void get_edges()
{
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}, dw[4] = {1, 2, 1, 2};
for (int z = 0; z < 2; z ++ )//这个是方向 先竖再横
for (int i = 1; i <= n; i ++ )//这个是坐标
for (int j = 1; j <= m; j ++ )
for (int u = 0; u < 4; u ++ )//枚举四个方向
if (u % 2 == z)//对2取模后刚好相等的就是对应的方向上的
{
int x = i + dx[u], y = j + dy[u], w = dw[u];
if (x && x <= n && y && y <= m)
{
int a = ids[i][j], b = ids[x][y];
if (a < b) e[k ++ ] = {a, b, w};//加一次就可以了 可以省点空间 但不加也可以啦
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1, t = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++, t ++ )
ids[i][j] = t;
for (int i = 1; i <= n * m; i ++ ) p[i] = i;
int x1, y1, x2, y2;
while (cin >> x1 >> y1 >> x2 >> y2)
{
int a = ids[x1][y1], b = ids[x2][y2];
p[find(a)] = find(b);
}
get_edges();
int res = 0;
for (int i = 0; i < k; i ++ )
{
int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
if (a != b)
{
p[a] = b;
res += w;
}
}
cout << res << endl;
return 0;
}
有点权 有边权
https://www.acwing.com/problem/content/1148/
建立虚拟源点 将点权变成边权 转化为最小生成树
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 305;
int w[N][N];
int n;
int d[N];
bool st[N];
int p(){
memset(d, 0x3f, sizeof d);
d[0]=0;
int res=0;
for (int i = 0; i <= n; i ++ ){
int t=-1;
for (int j = 0; j <= n; j ++ ){
if(!st[j]&&(t==-1||d[t]>d[j])){
t=j;
}
}
st[t]=true;
res+=d[t];
for (int j = 0; j <= n; j ++ ){
d[j]=min(d[j],w[t][j]);
}
}
return res;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++ ){
cin >> w[0][i];
w[i][0]=w[0][i];
}
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <= n; j ++ ){
cin >> w[i][j];
}
}
cout << p();
return 0;
}