方法1:dijkstra算法
思路:dijkstra算法求最短路,但是多了一个cost数组,cost[i] 表示修建到顶点i时,需要新修建的一条边。代码77行表示了最终加的边一定构成最短路,85行保证了在最短路的基础上修建更短的边,109行计算结果即可
可以忽略:
问题:
1.用优先队列,前n-1次更新,每次出队的是否一定是要选择的点?
否定。
证明:
#include<iostream>
using namespace std;
#include<cstring>
#include<queue>
#define N 10005
#define M 200001 //注意 是无向边,所以边的最大值要扩大二倍
int n,m;
int ans;
struct E{
int v;
int w;
}e[M];
struct Node{
int u;
int len;
Node(int uu,int ww):u(uu),len(ww){
}
bool operator< (const Node & nn) const{ //优先队列默认大顶堆,故需重载小于号
return len>nn.len;
}
};
priority_queue <Node> qq;
int h[N];
int ne[M];
int idx;
int dis[N];
int cos[N];
int vis[N]; //表面该点是否已经被更新
void add(int u,int v,int w)
{
e[idx].v=v;
e[idx].w=w;
ne[idx]=h[u];
h[u]=idx++;
}
void dj()
{
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
// vis[1]=1;
qq.push(Node(1,0));
while(!qq.empty())
{
Node tt=qq.top();
qq.pop();
int u=tt.u;
if(!vis[u]) //也可以不用vis数组
{
vis[u]=1;
}else continue;
for(int i=h[u];~i;i=ne[i])
{
int v=e[i].v;
int w=e[i].w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
cos[v]=w;
qq.push(Node(v,dis[v]));
}else if(dis[v]==dis[u]+w){
cos[v]=min(cos[v],w);
}
}
}
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof(h));
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dj();
for(int i=2;i<=n;i++)
ans+=cos[i];
cout<<ans;
return 0;
}
方法2:spfa
思路:先用spfa求出dis数组,也可用dijstra。然后每个顶点v,对于其所有邻接点u,如果满足dis[v]=dis[u]+w,则w可以列为备选修建边,从备选修建边选择最小的边即为修建边,见89开始的代码
可以忽略:
问题:为什么所有顶点选择最小修建边一定可以连通?
归纳假设法,
#include<iostream>
using namespace std;
#include<cstring>
#include<queue>
#define N 10005
#define M 200005
int h[N];
int idx;
int ne[M];
int n,m;
int ans;
int dis[N];
queue<int> qq;
int vis[N];
struct Eage{
int v;
int w;
Eage():v(),w(){}
Eage(int vv,int ww):v(vv),w(ww){}
}e[M];
void add(int u,int v,int w)
{
e[idx].v=v;
e[idx].w=w;
ne[idx]=h[u];
h[u]=idx++;
}
void spfa()
{
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1]=0;
vis[1]=1;
qq.push(1); //什么时候push 什么时候Push_back?
while(!qq.empty())
{
int tt=qq.front();
qq.pop();
vis[tt]=0;
for(int i=h[tt];~i;i=ne[i])
{
int v=e[i].v;
int w=e[i].w;
if(dis[v]>dis[tt]+w)
{
dis[v]=dis[tt]+w;
if(!vis[v])
{
qq.push(v);
vis[v]=1;
}
}
}
}
}
int main()
{
memset(h,-1,sizeof(h));
cin>>n>>m;
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
spfa();
for(int k=1;k<=n;k++)
{
int v=k;
int minn=1010;
for(int i=h[k];~i;i=ne[i])
{
int u=e[i].v;
int w=e[i].w;
if(dis[v]==dis[u]+w)
{
minn=min(minn,w);
}
}
if(minn!=1010)
ans+=minn;
}
cout<<ans;
return 0;
}