有向圖 N 點 M 邊,邊權 w。點 1 為起點,點 N 為終點(大神殿)。每個點 i 有 L_i 個「結界發生器」分佈在其他點,必須**先摧毀所有發生器**才能進入該點。摧毀發生器或大神殿需派遣一個自爆機器人(到達即引爆)。機器人無限,求摧毀大神殿的最短時間。
v 的前置條件:所有維持 v 結界的發生器點 u 已被到達(摧毀)。u → v 表示 u 是 v 的發生器,必須先「完成」 u。u → v 表示機器人可從 u 移動到 v,耗時 w。定義 f[v] = 到達並完成點 v(摧毀其發生器/神殿)的最早時間。
則 f[v] = max( 到達v的最早時間, max_{u∈發生器(v)} f[u] )。
其中「到達v的最早時間」取決於圖邊傳播:d[v] = min_{u→v} (f[u] + w)。
這是一個**動態規劃在圖上**的系統,可用**優先隊列(Dijkstra 風格)**求解。
u 維護 v 的列表 revDep[u],以及入度 deg[v] = 發生器數量。d[1]=f[1]=0,其餘 INF。入度為 0 的點入優先隊列(按 f 值小根堆)。u(當前 f[u] 最小且已確定):
u → v:d[v] = min(d[v], f[u] + w)。若 d[v] 更新且 d[v] ≥ f[v](滿足所有發生器),則 f[v] = d[v] 入堆。u → v(u 是 v 的發生器):deg[v]--,f[v] = max(f[v], f[u])。若 deg[v]==0 且 d[v] ≤ f[v],則 f[v] 已確定入堆。f[N] 即答案。
#include
using namespace std;
using ll = long long;
const ll INF = 4e18;
struct Edge { int to; ll w; };
struct DepEdge { int to; };
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m; cin >> n >> m;
vector> g(n+1);
for (int i=0;i>u>>v>>w;
if (u!=v) g[u].push_back({v,w});
}
vector> revDep(n+1);
vector deg(n+1);
for (int v=1; v<=n; ++v) {
int k; cin>>k; deg[v]=k;
for (int i=0;i>u;
revDep[u].push_back(v); // u 是 v 的發生器
}
}
vector d(n+1, INF), f(n+1, INF);
d[1]=f[1]=0;
using pli = pair;
priority_queue, greater> pq;
pq.push({0,1});
vector inQueue(n+1, 0);
inQueue[1]=1;
while (!pq.empty()) {
auto [fu, u] = pq.top(); pq.pop();
inQueue[u]=0;
if (fu != f[u]) continue; // 過時條目
// 1. 原圖邊鬆弛
for (auto& e : g[u]) {
int v = e.to;
if (d[v] > fu + e.w) {
d[v] = fu + e.w;
if (d[v] >= f[v] && !inQueue[v]) { // 所有發生器已滿足
f[v] = d[v];
pq.push({f[v], v});
inQueue[v]=1;
}
}
}
// 2. 依賴反邊更新
for (int v : revDep[u]) {
f[v] = max(f[v], fu);
if (--deg[v] == 0) { // 所有發生器完成
if (d[v] <= f[v] && !inQueue[v]) {
// f[v] 已是 max(發生器完成時間), d[v] 已是最早到達時間
// 實際完成時間 = max(f[v], d[v]) = f[v] (因d[v]<=f[v])
pq.push({f[v], v});
inQueue[v]=1;
}
}
}
}
cout << f[n] << '\n';
return 0;
}
f[u] 是當前未確定點中的最小值,且不再更新(因所有可能減小它的路徑/依賴已處理)。f[v] = max(最早到達時間, 所有發生器完成時間) 的定義在算法中通過 d[v](最早到達)與 deg[v](發生器計數)共同維護。deg[v]==0 且 d[v] ≤ f[v] 時,f[v] 確定為 max(f[v], d[v]) = f[v];若 d[v] > f[v],則需等待原圖邊鬆弛更新 d[v] 後再入堆。O((N+M) log N),每條邊/依賴最多鬆弛一次,堆操作 log N。O(N+M)。N≤3000, M≤70000 限制。