题目思路
把交叉路口看做图中的点,道路为边,根据以下三个条件:
改造的那些道路能够把所有的交叉路口直接或间接的连通起来。
在满足要求1的情况下,改造的道路尽量少。
在满足要求1、2的情况下,改造的那些道路中分值最大的道路分值尽量小。
由1.可知这个图是连通图;
由2.可知要连n-1条边;
由3.可知连的边的边权和要最小。
所以此题的考点为最小生成树
Prim
最小生成树的实现有两个算法——Prim和Kruskal,这里我们只介绍Prim算法
想法
Prim的想法是:每次寻找离MST集体最近的点,将点与链接集体的边加入MST
实现步骤
任取一个点
找不在MST中的离MST最近的点K
将K加入MST
更新其它点到MST的距离
重复直到所有点加入MST
程序实现
Prim的程序实现方法如下:
//dis表示点到MST的最短距离
//vis表示此点在不在MST里
//G为邻接表
//v为连接的点,w为边权
int prim()
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1]=0;
int maxx=0;//分值最大的那条路的分值
for(int i=1;i<=n;i++)//每一轮在MST里加一个点
{
int k=0;//k用来寻找不在MST的离MST最近的点
for(int j=1;j<=n;j++)//枚举每个点
if(!vis[j] && dis[j]<dis[k])//如果这个点不在MST内且比目前最近的点还近就替换成这个点
k=j;
if(k==0) break;//等于零证明完成或图不连通,退出
vis[k]=1;//将这个点加入MST
maxx=max(maxx,dis[k]);//更新分值最大的那条道路的分值
for(int j=0;j<G[k].size();j++)//更新其它点到MST的距离
{
int v=G[k][j].v,w=G[k][j].w;//提取连接的点和边权
if(!vis[v] && w<dis[v]) dis[v]=w;//如果它不在MST内且更近就更新
}
}
return maxx;
}
Code
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int N=10005;
struct Node
{
int v,w;//v为连接的点,w为边权
};
vector<Node> G[N];//G为邻接表
int dis[N],vis[N];//vis表示此点在不在MST里,dis表示点到MST的最短距离
int n,m,x,y,z;
int prim()
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1]=0;
int maxx=0;//分值最大的那条路的分值
for(int i=1;i<=n;i++)//每一轮在MST里加一个点
{
int k=0;//k用来寻找不在MST的离MST最近的点
for(int j=1;j<=n;j++)//枚举每个点
if(!vis[j] && dis[j]<dis[k])//如果这个点不在MST内且比目前最近的点还近就替换成这个点
k=j;
if(k==0) break;//等于零证明完成或图不连通,退出
vis[k]=1;//将这个点加入MST
maxx=max(maxx,dis[k]);//更新分值最大的那条道路的分值
for(int j=0;j<G[k].size();j++)//更新其它点到MST的距离
{
int v=G[k][j].v,w=G[k][j].w;//提取连接的点和边权
if(!vis[v] && w<dis[v]) dis[v]=w;//如果它不在MST内且更近就更新
}
}
return maxx;
}
int main()
{
cin>>n>>m;//读入数据
while(m--)
{
cin>>x>>y>>z;//读入数据
G[x].push_back(Node{y,z});//加入新点和边权
G[y].push_back(Node{x,z});//因为是无向图,还要反着加一遍
}
cout<<n-1<<" "<<prim();//输出答案
return 0;
}