abc369E Sightseeing Tour

chenfy27發表於2024-10-07

有N個島和M座雙向橋,編號為i的橋連線島U[i]和V[i],過橋耗時T[i],橋連線兩不同的島嶼,兩個島之間可能會有多座橋。
有Q組詢問,每次詢問給出K座橋,問從1號島到N號島的最少耗時,要求給出的K座橋分別至少經過1次。
2<=N<=400; N-1<=M<=2E5; 1<=U[i]<V[i]<=N; 1<=T[i]<=1E9; 1<=Q<=3000; 1<=K[i]<=5;

分析:跑floyd求出任意兩島之間的最短耗時,然後列舉所有可能的路徑,即對K座橋全排列,並列舉橋的兩端誰做入口誰做出口,將"1-橋-N"串起來得到耗時,取最小值。

#include <bits/stdc++.h>
using i64 = long long;

const i64 inf = 1E18;
i64 d[405][405];

void solve() {
	int N, M;
	std::cin >> N >> M;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			if (i == j) {
				d[i][j] = 0;
			} else {
				d[i][j] = inf;
			}
		}
	}

	std::vector<std::tuple<int,int,int>> edges(M+1);
	for (int i = 1; i <= M; i++) {
		int u, v;
		i64 t;
		std::cin >> u >> v >> t;
		edges[i] = {u, v, t};
		d[u][v] = std::min(d[u][v], t);
		d[v][u] = std::min(d[v][u], t);
	}

	for (int k = 1; k <= N; k++) {
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				d[i][j] = std::min(d[i][j], d[i][k] + d[k][j]);
			}
		}
	}

	auto getb = [&](const std::vector<int> &v, int st, i64 &w) {
		std::vector<int> res;
		for (size_t i = 0; i < v.size(); i++) {
			int x, y, z;
			std::tie(x, y, z) = edges[v[i]];
			if (st & (1 << i)) {
				res.push_back(y);
				res.push_back(x);
			} else {
				res.push_back(x);
				res.push_back(y);
			}
			w += z;
		}
		return res;
	};

	int Q;
	std::cin >> Q;
	for (int i = 1; i <= Q; i++) {
		int K;
		std::cin >> K;
		std::vector<int> B(K);
		for (auto &x : B) {
			std::cin >> x;
		}
		std::sort(B.begin(), B.end());

		i64 ans = inf;
		do {
			for (int j = 0; j < (1 << K); j++) {
				i64 res = 0;
				auto b = getb(B, j, res);
				res += d[1][b.front()] + d[b.back()][N];
				for (size_t k = 2; k < b.size(); k += 2) {
					res += d[b[k]][b[k-1]];
				}
				ans = std::min(ans, res);
			}
		} while (std::next_permutation(B.begin(), B.end()));
		std::cout << ans << "\n";
	}
}

int main() {
	std::cin.tie(0)->sync_with_stdio(0);
	int t = 1;
	while (t--) solve();
	return 0;
}