太原理工大学2021软件学院数据结构课程设计第二题(构造可以使n个城市连接的最小生成树)核心代码 Java+c(2021.6.6)
声明:这里只是给出核心代码
核心代码指程序的计算部分,不是完整程序
题目背景:P3366 【模板】最小生成树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目
题目描述
如题,给出一个无
向图,求出最小生成树,如果该图不连通,则输出 orz
。
输入格式
第一行包含两个整数 N
,M
,表示该图共有 N
个结点和 M
条无向边。
接下来 M
行每行包含三个整数xi
,yi
,zi
,表示有一条长度为zi
的无向边连接结点 xi
,yi
.
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
输入输出样例
**输入 **
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出 #1
7
最小生成树背景知识
1.最小生成树的代价指最小生成树各边长度之和
Kruscal算法代码
import java.io.*;
import java.util.Arrays;
import java.util.Comparator;
/**
* @see 图论 Kruscal https://www.luogu.com.cn/problem/P3366 https://www.acwing.com/solution/content/30279/
*/
public class Main {
static class Edge {
int a, b, w;
public Edge(int a, int b, int w) {
this.a = a;
this.b = b;
this.w = w;
}
}
static final int N = 100010;
static int n, m;
static int[] p;
static Edge[] edges;
static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static int nextInt() throws Exception {
in.nextToken();
return (int) in.nval;
}
static String next() throws Exception {
in.nextToken();
return in.sval;
}
/**
* 并查集find模板
* @param x 当前连通块的点
* @return
*/
static int find(int x) {
if (p[x]!=x){
p[x] = find(p[x]);
}
return p[x];
}
static int kruskal() {
Arrays.sort(edges, Comparator.comparingInt(a -> a.w));
for (int i = 1; i <= n; i++) {
p[i] = i;
}
int res = 0, count = 0;
for (int i = 0; i < m; i++) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
a = find(a);
b = find(b);
if (a != b) {
p[a] = b;
res += w;
count++;
}
}
//判断是否连通
return count < n - 1 ? -1 : res;
}
public static void main(String[] args) throws Exception {
n = nextInt();
m = nextInt();
edges = new Edge[m];
p = new int[n + 1];
for (int i = 0; i < m; i++) {
int a = nextInt();
int b = nextInt();
int c = nextInt();
edges[i] = new Edge(a, b, c);
}
int ans = kruskal();
if (ans==-1){
System.out.println("impr");
}else {
System.out.println(ans);
}
}
}
Kruscal算法代码(c++)
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
const ll N=2e5+10,INF=0x3f3f3f3f;
ll m,n,p[N];
struct edge{
ll a,b,w;
bool operator<(const edge &e)const { //重载小于号,使其按权重排序;
return w<e.w;
}
}es[N];
ll find(ll x)
{
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for(ll i=0;i<m;i++)
{
ll a,b,w;
cin>>a>>b>>w;
es[i]={a,b,w};
}
sort(es,es+m);
for(ll i=1;i<=n;i++) p[i]=i; //初始化父节点;
ll res=0,cnt=0; //res是最小生成树的权重之和;
//cnt是树中的边的数目,用于判断是否存在生成树;
for(ll i=0;i<m;i++)
{
ll a=es[i].a,b=es[i].b,w=es[i].w;
a=find(a),b=find(b); //如果ab不连通则联通;
if(a!=b)
{
p[a]=b;
res+=w;
cnt++; //边的数目加一;
}
}
if(cnt<n-1)cout<<"impossible"<<endl; //边数小于n-1条说明无生成树;
else cout<<res<<endl;
}
Prim算法代码(Java)
package Acwing858Prim算法求最小生成树;
import java.io.*;
import java.util.Arrays;
/**
* @see 图论 最小生成树 Prim https://www.acwing.com/solution/content/38312/ https://www.luogu.com.cn/problem/P3366
*/
public class PrimClass {
static final int INF = 9999;
static int[][] ad;
static int[] dist;
static int n, m;
static boolean[] st;
static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static int nextInt() throws Exception {
in.nextToken();
return (int) in.nval;
}
static int prim() {
Arrays.fill(dist, INF);
int res = 0;
for (int i = 0; i < n; i++) {
int t = -1;
for (int j = 1; j <= n; j++) {
if (!st[j] && (t == -1 || dist[j] < dist[t])) {
t = j;
}
}
if (i > 0 && dist[t] == INF) {
return INF;
}
if (i > 0) {
res += dist[t];
}
for (int j = 1; j <= n; j++) {
dist[j] = Math.min(dist[j], ad[t][j]);
}
st[t] = true;
}
return res;
}
public static void main(String[] args) throws Exception {
n = nextInt();
m = nextInt();
dist = new int[n+1];
ad = new int[n+1][n+1];
st = new boolean[n+1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i == j) {
ad[i][j] = 0;
} else {
ad[i][j] = INF;
}
}
}
while (m-- > 0) {
int a = nextInt();
int b = nextInt();
int c = nextInt();
ad[a][b] = Math.min(ad[a][b], c);
//-------------无向图-----------------------------
ad[b][a]= ad[a][b];
//-----------------------------------------------
}
int ans = prim();
if (ans == INF) {
System.out.println("orz");
} else {
System.out.println(ans);
}
}
}
Prim(c++)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510;
int g[N][N];//存储图
int dt[N];//存储各个节点到生成树的距离
int st[N];//节点是否被加入到生成树中
int pre[N];//节点的前去节点
int n, m;//n 个节点,m 条边
void prim()
{
memset(dt,0x3f, sizeof(dt));//初始化距离数组为一个很大的数(10亿左右)
int res= 0;
dt[1] = 0;//从 1 号节点开始生成
for(int i = 0; i < n; i++)//每次循环选出一个点加入到生成树
{
int t = -1;
for(int j = 1; j <= n; j++)//每个节点一次判断
{
if(!st[j] && (t == -1 || dt[j] < dt[t]))//如果没有在树中,且到树的距离最短,则选择该点
t = j;
}
st[t] = 1;// 选择该点
res += dt[t];
for(int i = 1; i <= n; i++)//更新生成树外的点到生成树的距离
{
if(dt[i] > g[t][i] && !st[i])//从 t 到节点 i 的距离小于原来距离,则更新。
{
dt[i] = g[t][i];//更新距离
pre[i] = t;//从 t 到 i 的距离更短,i 的前驱变为 t.
}
}
}
}
void getPath()//输出各个边
{
for(int i = n; i > 1; i--)//n 个节点,所以有 n-1 条边。
{
cout << i <<" " << pre[i] << " "<< endl;// i 是节点编号,pre[i] 是 i 节点的前驱节点。他们构成一条边。
}
}
int main()
{
memset(g, 0x3f, sizeof(g));//各个点之间的距离初始化成很大的数
cin >> n >> m;//输入节点数和边数
while(m --)
{
int a, b, w;
cin >> a >> b >> w;//输出边的两个顶点和权重
g[a][b] = g[b][a] = min(g[a][b],w);//存储权重
}
prim();//求最下生成树
//getPath();//输出路径
return 0;
}
再次强调,以上只是核心代码,同学们要根据核心代码,自己编写完整的程序
完整程序应该是这样
刷题不迷路,欢迎关注博主的git题集,这里有最详细的分类,最经典的例题和最全面的注释
感兴趣的同学可以赏个star吗:算法刷题集git地址 题目源码地址