AtCoder

ABC369 E問題(Sightseeing Tour)を解く

AtCoder_ABC369_E

AtCoder が提供しているABC(AtCoder Beginner Contest)369 E問題をC++で解いてみました。ABC369は、2024年8月31日21:00に実施されました。

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

E問題 Sightseeing Tour(Difficulty : 1301)

問題の詳細は、リンク先をご覧ください。

ABC369 E問題 Sightseeing Tour

ワーシャル・フロイド法で頂点間の最短距離を求めて全探索します。AtCoder Problems による Difficulty は 1301 でした

解答案

大きく分けてプログラムを2つに分けます。

最初に、ワーシャル・フロイド(Warshell Floyd)法 を使って、すべての頂点間の最短距離を求めます。頂点数 $N$ の最大値は $400$ です。ワーシャル・フロイド法の計算量 $O(N^3) = 400^3 = 6.4 \times 10^7$ であるため、時間内に処理できます。

次に最大5つの橋(グラフの辺)を選びます。頂点 $1$ から頂点 $N$ までの経路で、この5つの橋を通る経路は、辺の端が2通りあるため、$5! \times 2^5 = 3840$ 通りあります。全探索して最小値を求めます。問い合わせは $3000$ 回ありますが、全体の計算量は、約 $1.6 \times 10^7$ となり、この計算も時間内に処理できます。

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

前半のワーシャル・フロイド法の実装は、定番コードです(42ー64行目)。同じ頂点を結ぶ2つ以上の橋があることに注意します(52、53行目の処理)。

$5! \times 2^5 = 3840$ 通りの全探索は再帰関数 rec として実装しました(18ー36行目)。使っている辺を used で管理して、端を指定しながら、次の辺を探します。端が2通りあるため、再帰呼び出しも2か所あります(30、31行目)。

少し長くなりましたが、以下が、C++プログラムです。

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

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

int n;
vector<vector<ll>> dist(400, vector<ll>(400, INF));
vector<int> u;
vector<int> v;
vector<ll> t;

int k;
vector<int> b;
vector<bool> used;
ll result;

void rec(int num, ll w, int from)
{
	if (num == k) {
		result = min(result, w + dist[from][n - 1]);
		return;
	}

	for (int i = 0; i < k; ++i) {
		if (used[i]) {
			continue;
		}
		used[i] = true;
		rec(num + 1, w + dist[from][u[b[i]]] + t[b[i]], v[b[i]]);
		rec(num + 1, w + dist[from][v[b[i]]] + t[b[i]], u[b[i]]);
		used[i] = false;
	}

	return;
}

int main()
{
	int m;
	cin >> n >> m;
	for (int i = 0; i < n; ++i) {
		dist[i][i] = 0;
	}
	u.resize(m);
	v.resize(m);
	t.resize(m);
	for (int i = 0; i < m; ++i) {
		cin >> u[i] >> v[i] >> t[i];
		--u[i];
		--v[i];
		dist[u[i]][v[i]] = min(dist[u[i]][v[i]], t[i]);
		dist[v[i]][u[i]] = min(dist[v[i]][u[i]], t[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]);
				}
			}
		}
	}

	int q;
	cin >> q;
	for (int i = 0; i < q; ++i) {
		cin >> k;
		b.resize(k);
		for (int i = 0; i < k; ++i) {
			cin >> b[i];
			--b[i];
		}
		used.assign(k, false);
		result = INF;
		rec(0, 0, 0);
		cout << result << endl;
	}

	return 0;
}

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

Python版もコーディングしましたが、「Python (CPython 3.11.4)」、「Python (PyPy 3.10-v7.3.12)」のどちらも TLE 判定となりました。まだ Python に慣れていないため、記事での紹介を見送ります。

最後に

コンテスト中にこの問題を解くことができました。自分にとっての難易度は高く感じました。このようなことがあると、コンテスト参加を続けることができています。

ABC269からブログで紹介を始めて、この回で100回分のコンテストを紹介することができました(ABC316が欠番となっています)。

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

COMMENT

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

CAPTCHA