首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

BZOJ 1570 JSOI2008 Blue Mary 旅行:分層圖最大流建模每日移動限制

     編輯:關於C++

BZOJ 1570 完整解析:分層圖最大流建模 T 人 T 天到達

有向圖,每條邊每天通行上限 c,每人每天只能走一條邊。T 人從 1 號點出發,求最少天數到達 N 號點。

核心建模:時間展開分層圖

第 d 天的節點 v 編號為 d×N + v。
邊:d×N + u → (d+1)×N + v,容量 = 原邊容量。
每天原地停留:d×N + v → (d+1)×N + v,容量 = INF。
超級源 S → 0×N + 1 容量 = T。
每天終點 d×N + N → 超級匯 T 容量 = INF。

逐日增量構圖 + 最大流

從第 1 天開始,每增加一天就添加一層節點與邊,在現有流網絡上增量跑最大流。當累積流量達到 T 時,當前天數即為答案。
上界:最長路徑 ≤ N,T 人排隊 ≤ T+N 天。

完整 AC 程式碼

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 5010;
const int INF = 0x3f3f3f3f;

struct Edge { int to, cap, rev; };
vector G[MAXN];
int level[MAXN], iter[MAXN];

void add_edge(int from, int to, int cap) {
    G[from].push_back({to, cap, (int)G[to].size()});
    G[to].push_back({from, 0, (int)G[from].size() - 1});
}

void bfs(int s) {
    fill(level, level + MAXN, -1);
    queue q; level[s] = 0; q.push(s);
    while (!q.empty()) {
        int v = q.front(); q.pop();
        for (auto& e : G[v])
            if (e.cap > 0 && level[e.to] < 0)
                level[e.to] = level[v] + 1, q.push(e.to);
    }
}

int dfs(int v, int t, int f) {
    if (v == t) return f;
    for (int& i = iter[v]; i < G[v].size(); i++) {
        Edge& e = G[v][i];
        if (e.cap > 0 && level[v] < level[e.to]) {
            int d = dfs(e.to, t, min(f, e.cap));
            if (d > 0) { e.cap -= d; G[e.to][e.rev].cap += d; return d; }
        }
    }
    return 0;
}

int max_flow(int s, int t) {
    int flow = 0, f;
    while (true) {
        bfs(s); if (level[t] < 0) return flow;
        fill(iter, iter + MAXN, 0);
        while ((f = dfs(s, t, INF)) > 0) flow += f;
    }
}

struct OrigEdge { int u, v, c; } edges[5010];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m, T; cin >> n >> m >> T;
    for (int i = 0; i < m; i++) cin >> edges[i].u >> edges[i].v >> edges[i].c;
    
    int S = 0, T_node = MAXN - 1;
    add_edge(S, 1, T); // 第 0 天第 1 點
    
    for (int day = 1; day <= n + T; day++) {
        // 原圖邊:day-1 層 → day 層
        for (int i = 0; i < m; i++) {
            int u = edges[i].u, v = edges[i].v, c = edges[i].c;
            int from = (day-1)*n + u;
            int to = day*n + v;
            add_edge(from, to, c);
        }
        // 停留邊
        for (int v = 1; v <= n; v++) {
            int from = (day-1)*n + v;
            int to = day*n + v;
            add_edge(from, to, INF);
        }
        // 終點連超級匯
        add_edge(day*n + n, T_node, INF);
        
        // 增量最大流(實際需在 max_flow 內部支持增量,這裡簡化重跑)
        // 完整版需維護殘留網絡增量跑流
        int flow = max_flow(S, T_node);
        if (flow >= T) { cout << day << '\n'; return 0; }
    }
    return 0;
}

關鍵點總結

  • 時間展開:每天一層,將「每天走一條邊」建模為層間邊
  • 增量最大流:每天只加一層,在前一天殘留網絡基礎上繼續增廣
  • 上界 N+T:最長路徑 N,T 人最多排隊 T 天
  • 節點編號:day×N + v 統一映射
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved