BZOJ - 2407 探险 Dijkstra + 重构图

大家都很强, 我还是算了吧。

我是智障

我是智障

我是智障

重要的事情说三遍, Woc!

我是智障

不行我还要再说一遍!

我把g_[]数组大小开成了MAXN, 只有一个点。TMD也不RE,就是WA。

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

HINT

N<=10000,M<=200000,1<=W,V<=10000

不过能够想出最优的正确解法, 我也是很高兴的。但是WA了, 不开心啊不开心,郁闷。 大概思路如下:

先做一遍最短路,求出从S 到点i 的最短路中,第一个经过的点pre[i],即Sàpre[i]à….ài;若最短路为Sài,则pre[i]=i。求出pre后,重新构一遍图。令原图中点1 到i 的最短路为dist[i]。对于原图中的每一条边
(u,v,w):
1. 若u = S:
若v = pre[v],则略过;否则G’中连边(u,v,w);
2. 若v = S:
若u = pre[u],则G’中连边(u,T,w);否则连边(S,T,dist[u]+w);
3. 若u ≠ S 且v ≠ S:
若pre[u] = pre[v],则G’中连边(u,v,w);否则连边(S,v,dist[u]+w)。
这样在新图G’中求一遍S->T 的最短路即可。
这个构造很巧妙,它把每条S->i 的边拆开,这样就不会导致一条路走两遍(除形如S->i这种路外,其余的路没必要走)。构完图后,只需一遍最短路即可初解。

那么, 我的方法是虚拟一个点 n + 1,就可以把问题转化成由1 到 n + 1的最短路,SPFA会被卡, 但是某个神直接卡过常数, 强者, 他说, 要相信玄学!yoyoyo~

放代码

#include "queue"
#include "cstdio"
#include "cctype"
#include "cstdlib"
#include "cstring"

const int MOD = 1000000007;

#define abs(a)  ((a) > 0 ? (a) : (-a))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))

#define rep(_, m, n)  for(register int _ = (m); _ <= (n); ++_)
#define res(_, m, n)  for(register int _ = (m); _ >= (n); --_)
#define edges(u)  for(register int i = head[u]; i; i = g[i].pre)

template <class T>
inline bool readIn(T &x)  {
    T flag = 1;  char ch;
    while(!(isdigit(ch = (char) getchar())) && ch != -1)  if( ch == '-' )  flag = -1;
    if(ch == -1)  return false;
    for(x = ch - 48; isdigit(ch = (char) getchar()); x = (x << 1) + (x << 3) + ch - 48);
    x *= flag;
    return true;
}

template <class T>
inline void write(T x)  {
     if (x > 9)
     write(x / 10);
     putchar(x % 10 + 48);
}

template <class T>
inline bool writeIn(T x)  {
     if (x < 0)  {
         putchar('-');
         x = -x;
     }
     write(x);
     return true;
}

struct io  {
    #define SIM "exp"
    io()  {
        freopen(SIM ".in", "r", stdin);
        freopen(SIM ".out", "w", stdout);
    }
    ~io()  {
        fclose(stdin);
        fclose(stdout);
    }
} WhiteBunny;

const int MAXN = 10005, MAXM = 400005;

int n, m, p[MAXN];

namespace S  {
    class edge  {
    public:
        int to, pre, w;
        edge(int to = 0, int pre = 0, int w = 0) : to(to), pre(pre), w(w)  {  }
    } g[MAXM], g_[MAXM];//FUKKKKKKKKKKKKKKKKK!

    int ne, head[MAXN], vis[MAXN], dis[MAXN], head_[MAXN];

    inline bool adde(int u, int v, int w)  {
        g[++ne] = edge(v, head[u], w), head[u] = ne;
        return true;
    }
    inline bool _adde(int u, int v, int w)  {
        g_[++ne] = edge(v, head_[u], w), head_[u] = ne;
        return true;
    }

    class Dijkstra  {
    private:
        struct nod  {
            int id, w;
            nod(int id = 0, int w = 0) : id(id), w(w)  {  }
            inline bool operator < (const nod &rhs)  const  {
                return w > rhs.w;
            }
        };
        std::priority_queue<nod> q;
    public:
        void dijkstra()  {
            memset(dis, 0x3f, sizeof (int) * (n + 3));
            memset(vis, false, sizeof (int) * (n + 3));
            q.push(nod(1, 0));  dis[1] = 0;
            while( !q.empty() )  {
                int u = q.top().id; q.pop();
                if(vis[u])  continue;
                vis[u] = true;
                edges(u)  {
                    int v = g[i].to;
                    if( dis[v] > dis[u] + g[i].w  )  {
                        dis[v] = dis[u] + g[i].w;
                        q.push(nod(v, dis[v]));
                        !(u ^ 1) ? p[v] = v : p[v] = p[u];
                    }
                }
            }
        }
    } D;
}

using namespace S;

inline void rebuild_graph()  {
    ne = 0;
    rep(u, 1, n)  {
        edges(u)  {
            int v = g[i].to;
            if(  v == 1 )  {
                if(p[u] ^ u) _adde(1, n + 1, dis[u] + g[i].w);
                else _adde(u, n + 1, g[i].w);
            }
            if( !(u ^ 1) && (p[v] ^ v) )
                _adde(1, v, g[i].w);
            if((v ^ 1) && (u ^ 1))  {
                if(p[u] ^ p[v]) _adde(1, v, dis[u] + g[i].w);
                else _adde(u, v, g[i].w);
            }
        }
    }
    rep(i, 1, n + 1)  head[i] = head_[i];
    rep(i, 1, ne)  g[i] = g_[i];
}

inline void initialize()  {
    int s, t, v, w;
    readIn(n);readIn(m);
    rep(i, 1, m)  readIn(s), readIn(t), readIn(v), readIn(w), adde(s, t, v), adde(t, s, w);
}

int main()  {
    initialize();
    D.dijkstra();
    rebuild_graph();
    D.dijkstra();
    writeIn(dis[n + 1]);
    puts("");
    return 0;
}

大家都很强, 我还是可以吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值