单源最短路径问题,需要求出最短路径,然后找到一条从1号为起点的最小生成树。
由于n比较大,采用邻接表或者优先队列,这里把两个代码都写出来。
邻接表
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxx=1e5+10;
int d[maxx],n,m;
bool vis[maxx];
struct edge{
int to,len;
edge(int t,int l):to(t),len(l){
};
friend bool operator < (edge a,edge b){
return a.len<b.len;
}
};
vector<edge>g[maxx],pre[maxx];
void dijkstra(){
fill(d,d+maxx,inf);
fill(vis,vis+maxx,0);
d[1]=0;
for(int i=1;i<=n;i++){
int u=-1,minn=inf;
for(int j=1;j<=n;j++){
if(vis[j]==false&&d[j]<minn){
u=j;
minn=d[j];
}
}
vis[u]=true;
for(int j=0;j<g[u].size();j++){
int v=g[u][j].to,l=g[u][j].len;
if(vis[v]) continue;
else if(d[v]>d[u]+l){
d[v]=d[u]+l;
pre[v].clear();
pre[v].push_back(edge(u,l));
}
else if(d[v]==d[u]+l){
pre[v].push_back(edge(u,l));
}
}
}
}
int main(){
cin>>n>>m;
int a,b,c;
for(int i=0;i<m;i++){
cin>>a>>b>>c;
g[a].push_back(edge(b,c));
g[b].push_back(edge(a,c));
}
dijkstra();
int ans=0;
for(int i=1;i<=n;i++) sort(pre[i].begin(),pre[i].end());
for(int i=2;i<=n;i++) ans+=pre[i][0].len;
printf("%d\n",ans);
return 0;
}
优先队列算法实现
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 10000 + 10;
const int INF = INT_MAX;
struct Edge{ //用于图邻接表存储的边的信息
int to; //该边的另外一端的顶点号
int length; //该边的长度
Edge(int t,int l):to(t),length(l){}//构造函数
};
struct Node{ //各节点的信息
int number;//该节点的编号
int distance;//源点到该节点的最短路径长度
Node(){}
Node(int n,int d):number(n),distance(d){}
//由与点的信息要加入到优先队列中,故应设置各点大小关系
bool operator<(const Node& point) const{//小于号重载
return distance > point.distance;//距离大的优先级小
}
};
vector<Edge> graph[MAXN];//图的邻接表存储
int cost[MAXN];//最短路径前提下,从已处理的点到该点的最短距离
int dist[MAXN];//从源点(首都1)到各点的最短路径
bool visit[MAXN];//标记各个点是否被访问过
void Dijkstra(int start,int n){//Dijkstra算法
fill(dist,dist+n+1,INF);//将所有最短路径初始化为无穷大
fill(visit,visit+n+1,false);//将所有点标记为未访问过
fill(cost,cost+n+1,INF);//将所有点到已有集合的最短距离初始化为INF
dist[start] = 0;//修改源点的信息
cost[start] = 0;
priority_queue<Node>que;
que.push(Node(1,0));//将源点(首都1)加入到优先队列中
Node front;
while(!que.empty()){
front = que.top();
que.pop();
int current = front.number;//current记录对头结点的编号
if(!visit[current]){//若current没有被访问过
visit[current] = true;//将其标记为已访问过
//进行松弛操作,更新与该点相邻的所有点的信息
for(int i=0;i<graph[current].size();i++){
//获取current的邻接点now
int now = graph[current][i].to;
if(visit[now]){//若该点已被访问过,则处理下一个邻接点
continue;
}
//记录下current到now的距离
int spend = graph[current][i].length;
if(dist[now] > spend + dist[current]){
dist[now] = spend + dist[current];
cost[now] = spend;
//若该点的信息被更新,则加入到优先队列中
//一个点可能被添加多次,但通过visit数组
//可以保证其只被访问过一次(距离最短的那次)
que.push(Node(now,dist[now]));
}else if(dist[now] == spend + dist[current]){
cost[now] = min(cost[now],spend);//若相等,取小
}
}
}
}
}
int main(){
int n,m,a,b,c;
cin>>n>>m;
for(int i=0;i<m;i++){
cin>>a>>b>>c;
graph[a].push_back(Edge(b,c));//无向图
graph[b].push_back(Edge(a,c));
}
Dijkstra(1,n);//传入首都的位置 城市的个数
int answer = 0;
for(int i=1;i<=n;i++){//cost[]中各值的和即为答案
answer += cost[i];
}
cout<<answer;
return 0;
}