1. 程式人生 > >題解 P2296 【尋找道路】

題解 P2296 【尋找道路】

主要思路:DFS + Dijkstra + 堆優化 + 反向思維

要不是寫程式碼的時間問題我就用線段樹優化

首先,題目要求是路徑上的所有點的出邊所指向的點都直接或間接與終點連通。

我們可以先不考慮如何通過某點到達終點,我們想象如果通過一個點可以再到達終點,說明把邊反過來後,終點可以到達這個點。

那我們可以建反邊,通過DFS求出可以去向終點的點了。到達不了的不符合條件,我們可以直接刪點了。

dfs:

int vis[mn];//表示是否去過
void dfs(int x){
    if(vis[x]) return; vis[x] = 1;
    for (int i = hh[x]; i; i=ee[i].nxt) if (!vis[ee[i].v])
            dfs(ee[i].v);
    //printf("%d\n", x);
}

我們如何刪點?

因為我們最終還是要跑最短路,所以我們就可以把這條邊邊權開到不可能到達的長度。

像這樣

    go(x, 1, n, 1) 
        if (!vis[x]) 
            for (int i = hh[x]; i; i = ee[i].nxt) {
                int v = ee[i].v;
                for (int j = h[v]; j; j = e[j].nxt)
                    e[j].w = 100000;
            }

然後把反向的邊變成正邊(我是直接存的兩種邊,其實都一樣),然後跑Dijkstra + 堆優化就好了。注意,如果我們把邊權值變化後就不能拿bfs像原來一樣那麼做了

程式碼:

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
using namespace std;
#define go(i, j, n, k) for (int i = j; i <= n; i += k)
#define fo(i, j, n, k) for (int i = j; i >= n; i -= k)
#define rep(i, x) for (int i = h[x]; i; i = e[i].nxt)
#define mn 10010
#define mm 200010
#define inf 2147483647
#define ll long long
#define ld long double
#define fi first
#define se second
#define root 1, n, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define bson l, r, rt
//#define LOCAL
#define mod 
#define Debug(...) fprintf(stderr, __VA_ARGS__)
inline int read(){
    int f = 1, x = 0;char ch = getchar();
    while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
//This is AC head above...
struct edge{
    int f, v, w, nxt;
    edge(int _f = 0, int _v = 0, int _w = 1, int _nxt = 0) : f(_f), v(_v), w(_w), nxt(_nxt){}
} e[mm << 1], ee[mm << 1];
int p, h[mn],pp,hh[mn];
inline void add(int a, int b, int c = 1){
    e[++p].nxt = h[a], h[a] = p, e[p].f = a, e[p].v = b, e[p].w = c;
}
inline void fadd(int a,int b){
    ee[++pp].nxt = hh[a], hh[a] = pp, ee[pp].f = a, ee[pp].v = b;
}
//Dij + 堆優化
struct node{
    int u,v;
    bool operator <(const node &b) const{
        return u > b.u;
    }
};//過載運算子
int n,m,s,t;
int dis[mn];//起點到每個點的距離
priority_queue<node> q;//優先佇列
inline void Dij(int s){//s為起點
    go(i, 0, n, 1)
        dis[i] = inf;
    dis[s] = 0;
    node o;
    o.u = 0;o.v = s;
    q.push(o);
    while (q.size()){
        int u = q.top().v, d = q.top().u;
        q.pop();
        if(d!=dis[u])
            continue;
        rep(i,u){
            int v = e[i].v, w = e[i].w;
            if (dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                node p;
                p.u = dis[v], p.v = v;
                q.push(p);
            }
        }
    }
}
int vis[mn];
void dfs(int x){
    if(vis[x]) return; vis[x] = 1;
    for (int i = hh[x]; i; i=ee[i].nxt) if (!vis[ee[i].v])
            dfs(ee[i].v);
    //printf("%d\n", x);
}
inline void debug(){
    go(i, 1, p, 1) printf("%d->%d %d\n", e[i].f, e[i].v, e[i].w);
    go(i, 1, pp, 1) printf("%d->%d %d\n", ee[i].f, ee[i].v, ee[i].w);
}
int main(){
    n = read(), m = read();
    go(i,1,m,1){
        int a = read(), b = read();
        fadd(b, a);
        add(a, b);
    }
    s = read(), t = read();
    //debug();
    dfs(t);
    go(x, 1, n, 1) 
        if (!vis[x]) 
            for (int i = hh[x]; i; i = ee[i].nxt) {
                int v = ee[i].v;
                for (int j = h[v]; j; j = e[j].nxt)
                    e[j].w = 100000;
            }
    //debug();
    Dij(s);
    if(dis[t]!=inf)
        cout << dis[t];
    else
        cout << -1;
#ifdef LOCAL
    Debug("\nMy Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
    return 0;
}

第十六次發題解,希望幫助同學們建立反向思維