最短网络
题目描述
核心思路
从题意可知,农夫想要连接所有农场并且想要所使用的光纤最短。这其实就是想要求最小生成树,因为最小生成树一定包含所有节点(即所有农场),而且是连通的(连接所有农场),并且边权之和是最小的(所使用的光纤最短)。因此就是一道裸的最小生成树算法。
求解最小生成树有两种算法:prim算法和Kruskal算法。
- prim算法一般适用于稠密图,用邻接矩阵存储。而题目又说了对称矩阵,所以这题就可以用prim算法来求解
- Kruskal算法一般适用于稀疏图,我们一般用结构体来存储
代码
写法1:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110,INF=0x3f3f3f3f;
int n;
//prim算法比较适用于稠密图 稠密图可以用邻居矩阵来存储
//这里采用prim算法 所以用了邻居矩阵
int g[N][N];
//dist[i]表示节点i距离S集合的距离
int dist[N];
//st[i]=true表示节点i已经被加入了S集合
bool st[N];
//prim算法求最小生成树
int prim()
{
int res=0; //最小生成树的权值总和
memset(dist,0x3f,sizeof dist); //距离初始化为无穷大
//起点1号节点到自身距离为0
dist[1]=0;
//最终得到的最小生成树中一定有n个节点
//因此需要循环n次 每个把一个节点加入S集合中
//最终都会把这n个节点加入S集合中
for(int i=0;i<n;i++)
{
int t=-1; //寻找不在S集合中,但是距离S集合最近的一个节点
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
if(dist[t]==INF)
return INF;
res+=dist[t]; //累加最小生成树的权值和
st[t]=true; //标记节点t已经被加入了S集合
//遍历节点t的所有邻接点 通过t来更新这些邻接点到起点的距离
for(int k=1;k<=n;k++)
if(!st[k])
dist[k]=min(dist[k],g[t][k]);
}
return res;
}
int main()
{
scanf("%d",&n);
//读入邻接矩阵
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&g[i][j]);
printf("%d\n",prim());
return 0;
}
写法2:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110,INF=0x3f3f3f3f;
int n;
int g[N][N];
int dist[N];
bool st[N];
int prim()
{
int res=0;
memset(dist,0x3f,sizeof dist);
for(int i=0;i<n;i++)
{
int t=-1;
for(int j=1;j<=n;j++)
if(!st[j]&&(t==-1||dist[t]>dist[j]))
t=j;
if(i&&dist[t]==INF)
return INF;
if(i)
res+=dist[t];
st[t]=true;
for(int k=1;k<=n;k++)
dist[k]=min(dist[k],g[t][k]);
}
return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&g[i][j]);
printf("%d\n",prim());
return 0;
}