n 個變量 S₁..Sₙ,兩類約束:
求 S₁ 到 Sₙ 的最短路(即 Sₙ - S₁ 的最大可能值)。輸出:-1(負環無解)、-2(不可達)、或最短距離。
每個約束 S_v - S_u ≤ w 轉化為有向邊 u → v,權重 w。求 S₁ 到 Sₙ 最短路等價於求 Sₙ - S₁ 的最大值(因約束為 ≤)。
#include
#include
#include
#include
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1010;
struct Edge { int to, w, next; } e[30020];
int head[MAXN], ecnt;
int dist[MAXN], inq[MAXN], cnt[MAXN];
int n;
void addEdge(int u, int v, int w) {
e[ecnt] = {v, w, head[u]};
head[u] = ecnt++;
}
bool spfa(int s) {
memset(dist, 0x3f, sizeof(dist));
memset(inq, 0, sizeof(inq));
memset(cnt, 0, sizeof(cnt));
dist[s] = 0;
queue q;
q.push(s);
inq[s] = 1;
while (!q.empty()) {
int u = q.front(); q.pop();
inq[u] = 0;
if (++cnt[u] > n) return false; // 負環檢測
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to, w = e[i].w;
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
if (!inq[v]) { inq[v] = 1; q.push(v); }
}
}
}
return true;
}
int main() {
int T, x, y, a, b, c;
scanf("%d", &T);
while (T--) {
memset(head, -1, sizeof(head));
ecnt = 0;
scanf("%d%d%d", &n, &x, &y);
while (x--) {
scanf("%d%d%d", &a, &b, &c);
addEdge(a, b, c); // S_b - S_a ≤ c
}
while (y--) {
scanf("%d%d%d", &a, &b, &c);
addEdge(b, a, -c); // S_a - S_b ≤ -c
}
if (!spfa(1)) printf("-1\n");
else if (dist[n] != INF) printf("%d\n", dist[n]);
else printf("-2\n");
}
return 0;
}
| 概念 | 處理 |
|---|---|
| 差分約束 → 圖 | S_v - S_u ≤ w ⇒ u→v (w) |
| 求 Sₙ - S₁ 最大值 | 等價於 S₁ 到 Sₙ 最短路 (Bellman-Ford/SPFA) |
| 負環判斷 | SPFA 中某點入隊次數 > n ⇒ 負環 ⇒ 系統無解 (-1) |
| 不可達判斷 | dist[n] == INF ⇒ 1 不能推導出 n ⇒ 關系不確定 (-2) |
| 超級源點 | 本題從 1 出發單源,無需超級源點 |
HDU 3592 是差分約束 + SPFA 模板題。核心步驟:
注意:本題求 Sₙ - S₁ 最大值,對應最短路;若求最小值則對應最長路(需無正環)。