CCF 交通规划

题意大致如下:给你一张无向带权图,有标号为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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值