问题 A: 图的最小生成树-Prim算法
题目描述
Prim算法是求解带权图的最小生成树的经典算法。其步骤如下:
E1:任取一个顶点构成U={v0};构造向量cost[0…n-1]和adj[0…n-1],cost[i]表示顶点vi到U的最短边的长度,adj[i]表示顶点vi到U的最短边在U中的邻接点的下标;其中,vi∈V-U。初始时,生成树T为空集。
E2:重复n-1次
E21:从V-U中选出cost值最小的顶点vk,将边<vk, vadj[k]>加入到生成树T中,然后将vk并入U中;
E22:修正V-U中各顶点的cost值和adj值;
本题要求根据Prim算法,求解第一步状态下的cost和adj两个向量。
输入格式
输入为邻接矩阵存储的图,第一行为正整数n(小于100),表示图中顶点个数
接下来是n行,每行为n个空格隔开的非负整数。0表示两个顶点之间没有直达边,非0表示有直达边。且该数字为对应直达边的权重。
输出格式
假设第一步选择将序号最小的0号节点并入集合U。按照样例格式输出cost和adj两个向量
输入样例 复制
7
0 10 9 13 0 0 0
10 0 0 15 7 0 12
9 0 0 4 0 3 0
13 15 4 0 0 22 23
0 7 0 0 0 0 20
0 0 3 22 0 0 32
0 12 0 23 20 32 0
输出样例 复制
0 - -
1 10 0
2 9 0
3 13 0
4 - -
5 - -
6 - -
原码
#include<bits/stdc++.h>
using namespace std;
int main(){
int a;cin>>a;
int mat[a+5];
for(int i=0;i<a;i++){
cin>>mat[i];
if(mat[i])
cout<<i<<" "<<mat[i]<<" 0"<<endl;
else
cout<<i<<" - -"<<endl;
}
}
问题 B: 图的最小生成树-Kruskal算法
题目描述
Kruskal算法是最小生成树的经典算法,其步骤为:
E1:将所有的边按权值排序;
E2:设每个顶点为一个独立的点集,生成树T为空集;
E3:依序扫描每一条边<vi,vj>,直到已输出n-1条边:
E31:若vi、vj不在同一点集中,则将该边加入生成树T中,并合并这两个点集;否则舍弃该边;
本题要求读入带权图,对其所有边按权值排序后输出。
输入格式
输入为邻接矩阵存储的图,第一行为正整数n(小于100),表示图中顶点个数
接下来是n行,每行为n个空格隔开的非负整数。0表示两个顶点之间没有直达边,非0表示有直达边。且该数字为对应直达边的权重。
输出格式
对所有边按权重排序后输出。如果图只有1个点(即没有边),则直接输出空行。
输入样例
7
0 10 9 13 0 0 0
10 0 0 15 7 0 12
9 0 0 4 0 3 0
13 15 4 0 0 22 23
0 7 0 0 0 0 20
0 0 3 22 0 0 32
0 12 0 23 20 32 0
输出样例
<2,5>:3
<5,2>:3
<3,2>:4
<2,3>:4
<4,1>:7
<1,4>:7
<2,0>:9
<0,2>:9
<1,0>:10
<0,1>:10
<1,6>:12
<6,1>:12
<0,3>:13
<3,0>:13
<1,3>:15
<3,1>:15
<4,6>:20
<6,4>:20
<3,5>:22
<5,3>:22
<6,3>:23
<3,6>:23
<5,6>:32
<6,5>:32
数据范围与提示
注意题目的测试数据构造时用的排序算法如下:
for(int i=0;i<n;i++)
for(int j=i+1;j<n;j++)
if(a[j]<a[i]) swap(i,j); // 交换
#include<bits/stdc++.h>
using namespace std;
struct faze{
int row,col;
int info;
};
faze mat[123456];
void swap(int i,int j){
faze T;
T=mat[i];
mat[i]=mat[j];
mat[j]=T;
}
int main(){
int a;cin>>a;
if(a==1){
cout<<endl;return 0;
}
int cnt=0;
for(int i=0;i<a*a;i++){//临接矩阵转化为一维数组
cin>>mat[cnt].info;
if(!mat[cnt].info)continue;//剔除零元
mat[cnt].row=i/a;//记录行列
mat[cnt++].col=i%a;
}
for(int i=0;i<cnt;i++){//从小到大排序
for(int j=i+1;j<cnt;j++)
if(mat[j].info<mat[i].info)swap(i,j);
}
for(int i=0;i<cnt;i++){//输出
if(mat[i].info!=0){
printf("<%d,%d>:%d\n",mat[i].row,mat[i].col,mat[i].info);
}
}
return 0;
}
问题 C: 算法7-9:最小生成树
题目描述
最小生成树问题是实际生产生活中十分重要的一类问题。假设需要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路。这时,自然需要考虑这样一个问题,即如何在最节省经费的前提下建立这个通信网。
可以用连通网来表示n个城市以及n个城市之间可能设置的通信线路,其中网的顶点表示城市,边表示两个城市之间的线路,赋于边的权值表示相应的代价。对于n个顶点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。现在,需要选择一棵生成树,使总的耗费最小。这个问题就是构造连通网的最小代价生成树,简称最小生成树。一棵生成树的代价就是树上各边的代价之和。
而在常用的最小生成树构造算法中,普里姆(Prim)算法是一种非常常用的算法。以下是其算法的大致结构:
在本题中,读入一个无向图的邻接矩阵(即数组表示),建立无向图并按照以上描述中的算法建立最小生成树,并输出最小生成树的代价。
输入格式
输入的第一行包含一个正整数n,表示图中共有n个顶点。其中n不超过50。 以后的n行中每行有n个用空格隔开的整数,对于第i行的第j个整数,如果不为0,则表示第i个顶点和第j个顶点有直接连接且代价为相应的值,0表示没有直接连接。当i和j相等的时候,保证对应的整数为0。 输入保证邻接矩阵为对称矩阵,即输入的图一定是无向图,且保证图中只有一个连通分量。
输出格式
只有一个整数,即最小生成树的总代价。请注意行尾输出换行。
输入样例
4
0 2 4 0
2 0 3 5
4 3 0 1
0 5 1 0
输出样例
6
数据范围与提示
在本题中,需要掌握图的深度优先遍历的方法,并需要掌握无向图的连通性问题的本质。通过求出无向图的连通分量和对应的生成树,应该能够对图的连通性建立更加直观和清晰的概念。
#include<bits/stdc++.h>
using namespace std;
#define ff 0x3f
#define maxs 100
struct Gragh{
int vexnumber;
string vexinfo[maxs];
int matrix[maxs][maxs];
};
int prim(Gragh g){
int visit[maxs];int dis[maxs];//存放某点与其他点距离
int n=g.vexnumber;int res=0;
memset(visit,0,sizeof(visit));//开始全未访问
memset(dis,0x3f,sizeof(dis));//开始定为极大值
for(int i=0;i<n;i++){
int f=-1;
for(int j=0;j<n;j++){
if(!visit[j]&&(f==-1||dis[j]<dis[f])){
//开始f点选为0,后面选为dis数组为最小值且未访问的点
f=j;
}
}
if(i&&dis[f]==ff)return 0;
if(i)res+=dis[f];
for(int k=0;k<n;k++){//若各顶点与f点的距离小于dis,更新dis数组
dis[k]=min(dis[k],g.matrix[k][f]);
}
visit[f]=1;
}
return res;
}
int main(){
int a;cin>>a;
Gragh g;g.vexnumber=a;
for(int i=0;i<a;i++){
for(int j=0;j<a;j++){
cin>>g.matrix[i][j];
if(!g.matrix[i][j])
g.matrix[i][j]=ff;//不为0数调整为最大值
}
}
cout<<prim(g)<<endl;
}