【算法筆記】最短路總結

次短路

目前只會用dijstra求解。。(貌似有用spfa的。。
當然如果是DAG, 拓撲排序加DP也是可以的。。

次短路的求解和計數

hdu 3191

這道題有邊權爲0的情況。
dij所使用的貪心性質:S是目標集合(已經求得最短路或次短路的點),從 V - S 中選估計值最小的一個u,那麼d[u] 就是u的最短路長度。
如果邊權有0存在, 那麼這條性質就無法保證了。。
幸而數據比較特殊, 只要保證編號小的點先更新就行了。。

// 這道題的數據裏面有權值爲0的情況,pq並不能保證正確
// 詳見 http://acm.hdu.edu.cn/discuss/problem/list.php?problemid=3191
int n, m, src, dst;
struct Edge {
    int v, w;
};
vector<Edge> graph[N];
struct Node {
    int flag, d, i;
    bool operator < (const Node &rhs) const {
        if ( d != rhs.d ) return d > rhs.d;
        return i > rhs.i;
    }
};
const int INF = 1e9;
int d[2][N], cnt[2][N];
bool done[2][N];
void dij() {
    memset(done, 0, sizeof(done));
    memset(cnt, 0, sizeof(cnt));
    fill(d[0], d[0] + n, INF);
    fill(d[1], d[1] + n, INF);
    d[0][src] = 0;
    cnt[0][src] = 1;
    d[1][src] = -1;
    priority_queue<Node> q;
    q.push( (Node) {0, 0, src} );
    while(!q.empty()) {
        Node fr = q.top(); q.pop();
        int u = fr.i, du = fr.d;
        if ( done[fr.flag][u] ) continue;
        done[fr.flag][u] = 1;
        for (int i = 0; i < graph[u].size(); ++i) {
            int v = graph[u][i].v, w = du + graph[u][i].w, num = cnt[fr.flag][u];
            if ( d[0][v] > w ) {
                if ( d[0][v] != INF ) {
                    d[1][v] = d[0][v];
                    cnt[1][v] = cnt[0][v];
                    q.push( (Node) {1, d[1][v], v} );
                }
                d[0][v] = w;
                cnt[0][v] = num;
                q.push( (Node) {0, d[0][v], v} );
            } else if ( !done[0][v] && d[0][v] == w ) {
                cnt[0][v] += num;
            } else if ( d[1][v] > w ) {
                cnt[1][v] = num;
                d[1][v] = w;
                q.push( (Node) {1, d[1][v], v} );
            } else if ( !done[1][v] && d[1][v] == w ) {
                cnt[1][v] += num;
            }
        }
    }
    printf("%d %d\n", d[1][dst], cnt[1][dst]);
}

int main() {
#ifdef _LOCA_ENV_
    freopen("input.in", "r", stdin);
#endif // _LOCA_ENV
    while ( scanf("%d%d%d%d", &n, &m, &src, &dst) != EOF ) {
        rep(i, 0, n-1) graph[i].clear();
        rep(i, 1, m) {
            int x, y, w;
            scanf("%d%d%d", &x, &y, &w);
            graph[x].push_back( (Edge) {y, w} );
        }
        dij();
    }
    return 0;
}

次短路拓展

lightoj 1099

求無向圖上次短路值, 可以往回走。。
因爲是無向圖, 所以往回走並不影響什麼。。
只要“往回走”得到的次短路比較優, 貪心性質一定會保證它被選中。
也可以不用done數組, 直接用值比較來判斷這個點是否已經用來更新過了。。

int n, m, src, dst;
struct Edge {
    int v, w;
};
vector<Edge> graph[N];
struct Node {
    int flag, d, i;
    bool operator < (const Node &rhs) const {
        if ( d != rhs.d ) return d > rhs.d;
        return i > rhs.i;
    }
};
const int INF = 1e9;
int d[2][N];
bool done[2][N];
void dij() {
    memset(done, 0, sizeof(done));
    fill(d[0], d[0] + n, INF);
    fill(d[1], d[1] + n, INF);
    d[0][src] = 0;
    d[1][src] = INF;
    priority_queue<Node> q;
    q.push( (Node) {0, 0, src} );
    while(!q.empty()) {
        Node fr = q.top(); q.pop();
        int u = fr.i, du = fr.d;
        if ( done[fr.flag][u] ) continue;
        done[fr.flag][u] = 1;
        for (int i = 0; i < graph[u].size(); ++i) {
            int v = graph[u][i].v, w = du + graph[u][i].w;
            if ( d[0][v] > w ) {
                if ( d[0][v] != INF ) {
                    d[1][v] = d[0][v];
                    q.push( (Node) {1, d[1][v], v} );
                }
                d[0][v] = w;
                q.push( (Node) {0, d[0][v], v} );
            }  else if ( d[0][v] == w ) {
                // pass
            }  else if ( d[1][v] > w ) {
                d[1][v] = w;
                q.push( (Node) {1, d[1][v], v} );
            }
        }
    }
}

Dijstra拓展

當最短路徑有多條的時候, 我們可以倒過來遞推出, 所有最短路徑。
還可以對pq使用的HeapNode節點添加一些性質, 在不影響最短路值的情況下, 決定優先用哪些邊來更新。

codeforces 449B - Jzzhu and Cities

在不影響1到各點最短路值的情況下, 儘量去掉 給定邊集裏面的邊。

發佈了278 篇原創文章 · 獲贊 10 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章