購買物品 1 需滿足等級約束:交易雙方等級差 ≤ M。每件物品有價格、等級、若干「替代方案」(購買另一件物品 + 額外金額)。求獲得物品 1 的最小花費。N ≤ 100。
L1L1,且長度 ≤ Mlow = L1 - M ... L1,上界 high = low + MM+1 個窗口,每個窗口構建子圖跑最短路i -> j 代價 v 表示「買 i 可用 j + v 金額替代」cost[i][j](初始 INF,對角線 0)[L1-M+i, L1+i] (i=0..M)valid[] 标記dist[v](1 到各點最短路)min_{v valid} (dist[v] + price[v])#includeusing namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 105; int cost[MAXN][MAXN], price[MAXN], level[MAXN]; int dist[MAXN]; bool vis[MAXN], valid[MAXN]; int N, M; int dijkstra() { fill(dist, dist+N+1, INF); fill(vis, vis+N+1, false); dist[1] = 0; for (int iter = 1; iter <= N; ++iter) { int u = -1, best = INF; for (int i = 1; i <= N; ++i) if (valid[i] && !vis[i] && dist[i] < best) best = dist[i], u = i; if (u == -1) break; vis[u] = true; for (int v = 1; v <= N; ++v) if (valid[v] && !vis[v] && cost[u][v] < INF) dist[v] = min(dist[v], dist[u] + cost[u][v]); } int ans = INF; for (int i = 1; i <= N; ++i) if (valid[i]) ans = min(ans, dist[i] + price[i]); return ans; } int main() { while (cin >> M >> N) { fill(cost[0], cost[0]+MAXN*MAXN, INF); for (int i = 1; i <= N; ++i) cost[i][i] = 0; for (int i = 1; i <= N; ++i) { int x; cin >> price[i] >> level[i] >> x; for (int j = 0; j < x; ++j) { int t, v; cin >> t >> v; cost[i][t] = min(cost[i][t], v); // 可能有重複邊取小 } } int ans = INF; for (int i = 0; i <= M; ++i) { int low = level[1] - M + i; int high = level[1] + i; fill(valid, valid+N+1, false); for (int j = 1; j <= N; ++j) if (level[j] >= low && level[j] <= high) valid[j] = true; if (!valid[1]) continue; // 1 必須在窗口內 ans = min(ans, dijkstra()); } cout << ans << '\n'; } return 0; }
| 細節 | 處理 |
|---|---|
| 等級窗口枚舉 | i=0..M,窗口 [L1-M+i, L1+i] 滑動覆蓋所有合法範圍 |
| 圖邊方向 | i -> j 表示買 i 可替換為 j,故 Dijkstra 從 1 出發求反向最短路(或建反圖) |
| 多重邊 | cost[i][t] = min(cost[i][t], v) 保留最小代價 |
| 物品 1 必在窗口 | if (!valid[1]) continue 剪枝 |
| 價格加在最後 | dist[v] + price[v]:先到達 v 再買 v |
(節點, 屬性狀態)POJ 1062 通過「等級窗口枚舉 + 子圖 Dijkstra」優雅解決帶約束的最短路問題。核心技巧:
最短路 + 頂點權重此模式廣泛適用於「資源/屬性約束下的最優路徑」問題,如帶油量限制的最短路、帶時間窗的車輛路徑等。