[bzoj2407] dijkstra

这篇博客介绍了如何利用Dijkstra算法求解从原点出发到所有点的最短路径,并构建新图来处理特殊情况,包括不同路径的连接和权值计算。最终通过新图中的距离找到从原点到虚拟汇点的最短距离作为答案。
摘要由CSDN通过智能技术生成
**Description**

探险家小T好高兴!X国要举办一次溶洞探险比赛,获奖者将得到丰厚奖品哦!小T虽然对奖品不感兴趣,但是这个大振名声的机会当然不能错过!

比赛即将开始,工作人员说明了这次比赛的规则:每个溶洞和其他某些溶洞有暗道相连。两个溶洞之间可能有多条道路,也有可能没有,但没有一条暗道直接从自己连到自己。参赛者需要统一从一个大溶洞出发,并再次回到这个大溶洞。

如果就这么点限制,那么问题就太简单了,可是举办方又提出了一个条件:不能经过同一条暗道两次。这个条件让大家犯难了。这该怎么办呢?

到了大溶洞口后,小T愉悦地发现这个地方他曾经来过,他还记得有哪些暗道,以及通过每条暗道的时间。小T现在向你求助,你能帮他算出至少要多少时间才能回到大溶洞吗?

**Input**

第一行两个数n,m表示溶洞的数量以及暗道的数量。

接下来m行,每行4个数s、t、w、v,表示一个暗道连接的两个溶洞s、t,这条暗道正着走(s à t)的所需要的时间w,倒着走(t à s)所需要的时间v。由于溶洞的相对位置不同,w与v可能不同。

**Output**

输出一行一个数t,表示最少所需要的时间。

**Sample Input**

3 3
1 2 2 1
2 3 4 5
3 1 3 2

**Sample Output**

8

求出从原点出发到达每个点的最短距离d[i], 记录该路径从原点出发到达的第一个点p[i],创建虚拟汇点T,源点S,构建新图。

先依次枚举所有边

1.该边为(u,1, w)
如果u!= p[u],则没有经过这条边,则边(u,1)可以被引用,存在原点S->p[u]->….->u->S的路径,则直接加入(1,T,d[u]+w)的边,否则,若u == p[u] 说明到达点u的最短路径经由边(1,u),所以不能通过d[u]+w的方式返回原点。 但如果存在其他方式到达点u,则可以通过该边返回,故在新图中创建边(u,T,w)

2.该边为(1,v,w)
如果v!=p[v],则最短路径非p[v],故添加(1,v,w)
如果v==p[v],最短路径即p[v],不用添加

3.该边为(u,v,w)
若p[u] != p[v],说明从原点到达点u的最短路径,与从原点到达点v的最短路径不同,即存在原点S -> p[u] -> u -> v 的路径,创建边(1,v,d[u]+w)
若p[u] == p[v],则在新图中保留原边 (u,v,w)

答案为d[T].
(每个点到T的距离都是从原点出发到T点的最短距离)

[7.4]终于权限题可AC了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<cstring>
#define pa pair<int,int>
const int INF = 1000000000;
const int N = 10005;
const int M = 400005;
using namespace std;
int read() {
    int x = 0, f = 1;char ch = getchar();
    while( ch < '0' || ch > '9'){if(ch == '-')f=-1;ch=getchar();}
    while(ch>='0' && ch <='9'){x = x*10+ch-'0';ch = getchar();}
    return x*f;
}
int n,m,T,cnt,tot;
bool vis[N];
int u[M], v[M], w[M];
int last[N],p[N],dis[N];
struct Node {
    int to,next,v;
}e[M];
void insert( int u, int v, int w ) {
    e[++cnt].to = v;e[cnt].next = last[u];last[u] = cnt;e[cnt].v=w;
}
int ins( int a, int b, int c ) {
    u[++tot] = a;v[tot] = b;w[tot] = c;
}
void dijkstra() {
    memset(vis,0,sizeof(vis));
    priority_queue<pa,vector<pa>,greater<pa> >q;
    for( int i = 1; i <= T; i++ ) dis[i] = INF;
    dis[1] = 0; q.push(make_pair(0,1));
    while( !q.empty() ) {
        int now = q.top().second; q.pop();
        if( vis[now] ) continue;
        vis[now] = 1;
        for( int i = last[now]; i; i = e[i].next )
            if(dis[now] + e[i].v < dis[e[i].to] && !vis[e[i].to]) {
                dis[e[i].to] = dis[now] + e[i].v;
                if( now == 1 ) p[e[i].to] = e[i].to;
                else p[e[i].to] = p[now];
                q.push(make_pair(dis[e[i].to],e[i].to));
            }
    }
}
void rebuild() {
    for( int x = 1; x <= n; x++ ) 
        for( int i = last[x]; i; i = e[i].next ) {
            if( e[i].to == 1 ) {
                if( p[x] != x ) ins(1,T,dis[x]+e[i].v);
                else ins(x,T,e[i].v);
            } if( x == 1 )
                if( p[e[i].to] != e[i].to )
                    ins(1,e[i].to,e[i].v);
            if( e[i].to != 1 && x != 1) {
                if( p[x] != p[e[i].to]) ins(1,e[i].to,dis[x]+e[i].v);
                else ins(x,e[i].to,e[i].v);
            }
        }
        cnt = 0;
        memset(last,0,sizeof(last));
        for( int i = 1; i <= tot; i++ )
            insert(u[i],v[i],w[i]);
}
int main() {
    n = read();m = read();T = n;
    for( int i = 1; i <= m; i++ ) {
        int u = read(),v = read(),w1 = read(),w2 = read();
        insert(u,v,w1);
        insert(v,u,w2);
    }
    dijkstra();
    T++;
    rebuild();
    dijkstra();
    if( dis[T] == INF ) {
        printf("-1\n");
        return 0;
    }
    printf("%d\n",dis[T]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值