AtCoder

ABC338 F問題(Negative Traveling Salesman)を解く

AtCoder_ABC338_F

AtCoder が提供しているABC(AtCoder Beginner Contest)338 のF問題をC++で解いてみました。ABC338は、2024年1月27日21:00に実施されました。

AtCoder の紹介はこちらに、プログラミングの方針はこちらに記事があります。

F問題 Negative Traveling Salesman(Difficulty : 1834)

問題はリンク先をご覧ください。

ABC338 F問題 Negative Traveling Salesman

巡回セールスマン問題と同じように bitDP を使います。AtCoder Problems による Difficulty は 1835 でした。

解答案

負閉路が存在しないため、N頂点間の距離を Warshell Floyd 法で求めることができます。あとは、巡回セールスマン問題と同じです。ただし、巡回ではなく、すべての頂点を通る経路を考えます。

C++ プログラム例(ABC338F)

実装は、大きく分けて2つに分けられます。

  • Warshell Floyd 法でN頂点間の距離を求めます。19から35行目は、定番コードです。
  • 巡回セールス問題として解きます。
    • どの頂点から始めてもよいため、すべての頂点から始まる重みを0に初期化します(38ー40行目)。
    • 残りは、bitDP の定番コードです(41ー59行目)。
  • すべての頂点を通る重みの最小値を出力します(61ー70行目)。

以下が、C++プログラムとなります。

#include <bits/stdc++.h>
using namespace std;

typedef long long int ll;
#define INF (1LL << 60)

int main()
{
	int n, m;
	cin >> n >> m;
	vector<int> u(m), v(m);
	vector<ll> w(m);
	for (int i = 0; i < m; ++i) {
		cin >> u[i] >> v[i] >> w[i];
		--u[i];
		--v[i];
	}

	vector<vector<ll>> dist(n, vector<ll>(n, INF));
	for (int i = 0; i < n; ++i) {
		dist[i][i] = 0;
	}
	for (int i = 0; i < m; ++i) {
		dist[u[i]][v[i]] = w[i];
	}

	for (int k = 0; k < n; ++k) {
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < n; ++j) {
				if ((dist[i][k] < INF)&&(dist[k][j] < INF)) {
					dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
				}
			}
		}
	}

	vector<vector<ll>> dp(1 << n, vector<ll>(n, INF));
	for (int i = 0; i < n; ++i) {
		dp[1 << i][i] = 0;
	}
	for (int i = 1; i < (1 << n); ++i) {
		for (int j = 0; j < n; ++j) {
			if ((i & (1 << j)) == 0) {
				continue;
			}
			if (dp[i][j] == INF) {
				continue;
			}
			for (int k = 0; k < n; ++k) {
				if (dist[j][k] == INF) {
					continue;
				}
				if ((i & (1 << k)) == 0) {
					int v = (i | (1 << k));
					dp[v][k] = min(dp[v][k], dp[i][j] + dist[j][k]);
				}
			}
		}
	}

	ll result = INF;
	for (int i = 0; i < n; ++i) {
		result = min(result, dp[(1 << n) - 1][i]);
	}

	if (result == INF) {
		cout << "No" << endl;
	} else {
		cout << result << endl;
	}

	return 0;
}

AC(Accepted=正しいプログラム)と判定されました。

今回は、Python版の紹介は省略します。

最後に

前日にABC301E問題解説記事)を紹介しました。自分にとって、青Diffの問題は、かなり難しいと感じます。似た問題を続けて解くことによって、いつかコンテスト中に解ければと思います。

引き続き ABC の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA