题意大致如下:给你一张无向带权图,有标号为1到n的节点,现在要你选择一些边留下来,使得在留下来的边构成的新的图里,每个节点到节点1的最小距离不变(即和初始图一样),在满足上述要求的前提下,要使留下来的边的权重和最小,并求出这个最小权重和。
题解:只要我们用dijsktra算法加以改造遍历一遍原图,就能解决这个问题。而本题与一般的dijsktra模板题不同的地方在于,一般dijsktra只是求出源点到其他个点的最短距离,但并不考虑和记录路径,但本题要求的是权重和的最小值,也就是说我们要知道的是最后我们选择了哪些边留下来,或者说我们要知道留下来的所有边的权重是多少。为此,我建立了一个结构体叫Node,它有成员dist和cost,dist是表示它到源点的最小距离,而cost是表示为了使它和源点联通的最小花费(这个联通要保证是最短距离的前提下)。至于更新,dist更新和正常dijsktra更新时一样,先找到一个目前没有被加入集合的dist最小的节点u,然后用u来更新周边节点的dist,cost更新则是随着dist的更新进行的(如此保证这个cost满足最小距离这个前提),并且这个cost应该就等于节点u和被更新节点v的边的权重,即有Node[v].cost=weight[u][v]。最后,要注意的就是,有些节点的最短路径可能不止一条,而且,这些节点被开始的u节点更新得到的cost会要比后面的u节点更新得到的cost要大,所以当有Node[v].dist==Node[u].dist+weight[u][v]时,要注意更新Node[v].cost。(根据题目范围,dijkstra算法应用优先队列进行优化)
输入: 两个整数n,m。n是节点数,m是边数。就下来m行,每行三个数u,v,w,代表节点u,v之间有一条权重为w的边。
输出:一个整数,最小权重和。
代码如下:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
using namespace std;
const int maxn=10100;
const int maxm=101010;
const int inf=0x08080808;
int n,m;
int head[maxn],icount,vis[maxn];
struct Edge{
int v,w;
int next;
}edge[maxm*2];
struct Node{
int dist;
int cost;
}node[maxn];
struct Dist{
int dist;
int num;
bool operator <(const Dist& a)const {
return dist>a.dist;
}
};
priority_queue<Dist> pq;
void insert_edge(int u,int v,int w){
edge[icount].v=v;
edge[icount].w=w;
edge[icount].next=head[u];
head[u]=icount++;
}
void Dij(int s){
for(int i=1;i<=n;++i){
node[i].dist=node[i].cost=inf;
vis[i]=0;
}
Dist temp={0,s};
node[s].dist=node[s].cost=0;
pq.push(temp);
for(int i=1;i<n;++i){
temp=pq.top();
while(vis[temp.num]){
pq.pop();
temp=pq.top();
}
s=temp.num;
for(int j=head[s];j!=-1;j=edge[j].next){
int v=edge[j].v,w=edge[j].w;
if((node[s].dist+w)<=node[v].dist){
node[v].dist=node[s].dist+w;
temp.num=v;
temp.dist=node[v].dist;
pq.push(temp);
if(w<node[v].cost)
node[v].cost=w;
}
}
vis[s]=1;
}
}
int main(){
int u,v,w;
icount=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
head[i]=-1;
for(int i=0;i<m;++i){
scanf("%d%d%d",&u,&v,&w);
insert_edge(u,v,w);
insert_edge(v,u,w);
}
Dij(1);
int ans=0;
for(int i=1;i<=n;++i){
ans+=node[i].cost;
}
printf("%d\n",ans);
return 0;
}