poj1511 Invitation Cards (多源单汇最最短路)

问题描述

在电视时代,很少有人去剧院演出。马里迪内西亚的古代喜剧演员们知道这个事实。他们想要宣传戏剧,尤其是古典喜剧。他们印制了载有所有必要资料和节目的邀请卡。许多学生被雇来把这些请帖分发给人们。每个学生志愿者都指定了一个公交车站,他或她会在那里呆上一整天,邀请人们乘坐公交车。他们开设了一门特殊的课程,让学生们学习如何影响他人,以及影响与抢劫之间的区别。
交通系统非常特殊:所有线路都是单向的,而且恰好连接两个站点。每半小时有一辆公共汽车从始发站开出。到达目的地站后,它们返回空的起始站,在那里它们等待到下一个完整的半小时,例如X:00或X:30,其中“X”表示时间。两站之间的交通费由专用表格支付,并在现场支付。这些线路的规划是这样的,每次往返(即在同一站出发和结束的旅程)都要经过一个中央检查站(CCS),在那里每位乘客必须通过包括身体扫描在内的全面检查。
所有的ACM学生成员每天早上离开CCS。每位志愿者都要在预定的一站下车,邀请乘客。志愿者和车站一样多。最后,所有的学生都回到CCS。你要编写一个电脑程序,帮助ACM将每天运送员工的费用降到最低。

输入

输入由N种情况组成。输入的第一行只包含正整数n。每种情况都以一行开始,其中恰好包含两个整数P和Q, 1 <= P,Q <= 1000000。P为包括CCS在内的站点数,Q为公交线路数。然后是Q线,每条线描述一条总线。每一行都包含三个数字——起始站、目的地站和价格。CCS是由1号指定的。价格是正整数,其和小于1000000000。你也可以假设从一站到另一站总是可能的。

输出

对于每个案例,打印一行包含ACM每天为其志愿者的旅费支付的最低金额。

Sample Input

2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50

Sample Output

46
210

题意:

求起点到每个点的最短距离和,加上每个点回到终点的最短距离和(有向图来回距离不一样)

分析:

从起点到其他点直接正常最短路就行了很简单
主要是回来那段,如果每个点都跑一次最短路显然不行。
巧妙的想法是把所有边起点和终点反过来,这时候从起点跑出来的最短路就相当于从其他所有点到起点的最短路。

妙啊

code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<vector>
#include<cstring>
#include<queue>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int N=1e6+5;
const int M=2e6+5;
int read(){
    int x=0,f=0;char c=getchar();
    while(c!='-'&&!isdigit(c))c=getchar();
    if(c=='-')f=1,c=getchar();
    while(isdigit(c))x=x*10+c-'0',c=getchar();
    return f?-x:x;
}
ll head[N],nt[M],to[M],w[M];//存正常的边
ll head2[N],nt2[M],to2[M],w2[M];//存反转之后的边
ll d[N];
bool mark[N];
int n,m,cnt,cnt2;
void init(){
    memset(head,-1,sizeof head);
    memset(head2,-1,sizeof head2);
    cnt=cnt2=0;
}
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
    cnt2++;nt2[cnt2]=head2[y];head2[y]=cnt2;to2[cnt2]=x;w2[cnt2]=z;//建立反转的边
}
void spfa(int st){
    queue<int>q;
    memset(mark,0,sizeof mark);
    memset(d,inf,sizeof d);
    q.push(st);
    mark[st]=0;
    d[st]=0;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        mark[x]=0;
        for(int i=head[x];i!=-1;i=nt[i]){
            int v=to[i];
            if(d[v]>d[x]+w[i]){
                d[v]=d[x]+w[i];
                if(!mark[v]){
                    mark[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
void spfa2(int st){
    queue<int>q;
    memset(mark,0,sizeof mark);
    memset(d,inf,sizeof d);
    q.push(st);
    mark[st]=0;
    d[st]=0;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        mark[x]=0;
        for(int i=head2[x];i!=-1;i=nt2[i]){
            int v=to2[i];
            if(d[v]>d[x]+w[i]){
                d[v]=d[x]+w[i];
                if(!mark[v]){
                    mark[v]=1;
                    q.push(v);
                }
            }
        }
    }
}
int main(){
    int T=read();
    while(T--){
        init();
        n=read(),m=read();
        for(int i=0;i<m;i++){
            int a=read(),b=read(),c=read();
            add(a,b,c);
        }
        spfa(1);
        ll ans=0;
        for(int i=1;i<=n;i++){
            ans+=d[i];
        }
        spfa2(1);
        for(int i=1;i<=n;i++){
            ans+=d[i];
        }
        cout<<ans<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值