int n, m, cnt[N]; vector<pair<int, i64>> G[N]; i64 d[N], h[N]; bool vis[N], inq[N];
voidDijkstra(int s){ fill(d, d + n + 1, INF); memset(vis, 0, sizeof vis); #define pil pair<int, i64> priority_queue<pil, vector<pil>, greater<pil>> q; q.emplace(d[s] = 0, s); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (auto [v, w] : G[u]) if (d[v] > d[u] + w) q.emplace(d[v] = d[u] + w, v); } }
voidSPFA(int s){ fill(d, d + n + 1, INF); d[s] = 0; queue<int> q; q.push(s); inq[s] = 1; while (!q.empty()) { int u = q.front(); q.pop(); inq[u] = 0; for (auto [v, w] : G[u]) if (d[v] > d[u] + w) { d[v] = d[u] + w; cnt[v] = cnt[u] + 1; if (cnt[v] > n + 1) cout << "-1\n", exit(0); if (!inq[v]) q.push(v), inq[v] = 1; } } }
intmain(void){ ios::sync_with_stdio(0); cin >> n >> m; while (m--) { int u, v, w; cin >> u >> v >> w; G[u].emplace_back(v, w); } for (int i = 1; i <= n; ++i) G[0].emplace_back(i, 0); SPFA(0); for (int u = 1; u <= n; h[u] = d[u], ++u) for (auto& [v, w] : G[u]) w += d[u] - d[v]; for (int i = 1; i <= n; ++i) { Dijkstra(i); i64 ans = 0; for (int j = 1; j <= n; ++j) { if (d[j] == INF) ans += 1ll * j * INF; else ans += j * (d[j] + h[j] - h[i]); } cout << ans << '\n'; } return0; }
给定一个 n 个点 m 条边无向图 G=(V,E),再给定包含 k 个节点的点集 S,选出 G 的连通子图 G′=(V′,E′),要求:
S⊆V′,
E′ 中所有边的权值和最小。
你需要求出这个最小权值和,n≤100,m≤500,k≤10。
并不是直接将 S 连接起来就是最小的,可能需要借助剩下的 n−k 个点。这种问题可以使用状压 DP 来解决:
设 f(i,S) 表示以 i 为根的一棵树,包含集合 S 中所有点的最小边权值和。有转移:f(i,S)←min{f(i,T)+f(i,S−T)},f(i,S)←min{f(j,S)+w(i,j)}。前者可以使用子集 DP 实现,后者可以跑一个最短路(由于图很难特殊构造而且规模很小,所以实际上更建议 SPFA)。代码。