Prim求次小生成树
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N = 1005;
/*
在最小生成树中,i~j没有直接相连的边时,直接连接(i,j),这样就会形成一个环,再去掉环中 且除去刚连的边(i,j)外 最大的边;
即去掉在生成树上,i~j 最大的边。用边 (i,j) 替换掉最小生成树里i到j最大的边
去掉这个最大的边,得到的就是第二大的这个环上所有边权值和。
依次遍历每两个点,获取替代后最小的,就是次小生成树
*/
int G[N][N], used[N][N], mxd[N][N];
int n, m;
void init()
{
memset(used, 0, sizeof(used));
memset(mxd, 0, sizeof(mxd));
memset(G, INF, sizeof(G));
}
int Prim(int s)
{
int vis[N], dis[N], p[N];
memset(vis, 0, sizeof(vis));
for(int i = 1; i<=n; i++)
{
dis[i] = G[s][i];
p[i] = s;//p[i] i与最小生成树相连的一个端点
}
vis[s] = 1;
int sum = 0;
for(int i = 1; i<n; i++)
{
int mn = INF, u = s;
for(int j = 1; j<=n; j++)
{
if(!vis[j] && dis[j]<mn)
mn = dis[u=j];
}
vis[u] = 1;
sum += mn;
used[u][p[u]] = used[p[u]][u] = 1;//标记 u<->p[u] 这条边在最小生成树上
for(int j = 1; j<=n; j++)
{
if(vis[j] && j!=u)//遍历最小生成树上每个点,获取j 到u 路径上最长的边
mxd[j][u] = mxd[u][j] = max(mxd[j][p[u]], dis[u]);//dis[u] 是u<->p[u]的距离,mxd[j][p[u]]是j到p[u]的最长边
//j -> p[u] <-> u 通过这样一条路径来得到j->u路径上最长的边
if(!vis[j])//对于没在最小生成树的点
if(dis[j]>G[u][j])
{
dis[j] = G[u][j];//更新其到最小生成树的距离
p[j] = u;//更新点 j 连接最小生成树的点
}
}
}
return sum;
}
int SecPrim(int x)
{
int ans = INF;
for(int i = 1; i<=n; i++)
for(int j = 1; j<=n; j++)
if(i!=j && !used[i][j])//去掉最小生成树上 i,j 之间的最大的边值,加上i-j这条边
ans = min(ans, x-mxd[i][j]+G[i][j]);//依次枚举,找到最小的就是次小生成树
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
for(int i = 1; i<=m; i++)
{
int u, v, w;
scanf("%d%d%d",&u,&v,&w);
G[u][v] = G[v][u] = w;
}
int temp = Prim(1);
int ans = SecPrim(temp);
printf("最小生成树:%d\n次小生成树:%d\n", temp, ans);
}
return 0;
}
Kruskal求次小生成树
#include<bits/stdc++.h>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n,m;
int root[N], mxd[N][N];
vector<int> p[N];
struct node
{
int u,v,w,vis;//vis判断是否在最小生成树中
}G[N*N];
void init()
{
for(int i = 0; i<N; i++)
{
root[i] = i;
p[i].clear();
p[i].push_back(i);
}
}
int find_(int x)
{
return x == root[x] ? x : root[x] = find_(root[x]);
}
bool cmp(node a, node b)
{
return a.w<b.w;
}
void kruskal()
{
int ans = 0, sec_ans = INF;
sort(G, G+m, cmp);
init();
for(int i = 0; i<m; i++)
{
int ra = find_(G[i].u);
int rb = find_(G[i].v);
if(ra!=rb)
{
root[ra] = rb;
G[i].vis = 1;
ans += G[i].w;
//遍历两个不同集合中的每个点,更新他们之间的最长边
//ra集合里每个点 到rb集合里每个点 的最长边就是当前的w,
//因为kruskal是从小到大遍历每条边的,前面遍历的边一定比当前边小
for(int j = 0; j<p[ra].size(); j++)
for(int k = 0; k<p[rb].size(); k++)
{
int u = p[ra][j], v = p[rb][k];
mxd[u][v] = mxd[v][u] = G[i].w;
}
//再把两个集合合在一起
for(int j = 0; j<p[ra].size(); j++)
p[rb].push_back(p[ra][j]);
}
}
for(int i = 0; i<m; i++)
if(!G[i].vis)//找不在最小生成树里的边
sec_ans = min(sec_ans, ans - mxd[G[i].u][G[i].v] + G[i].w);//用当前边替换当前边两端点之间最长边
printf("最小生成树权值和:%d\n", ans);
printf("次小生成树权值和:%d\n", sec_ans);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i = 0; i<m; i++)
{
scanf("%d%d%d",&G[i].u, &G[i].v, &G[i].w);
G[i].vis = 0;
}
kruskal();
}
return 0;
}
测试案例
input
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
output
最小生成树权值和:3
次小生成树权值和:4
最小生成树权值和:6
次小生成树权值和:6