AtCoder

ABC328 E問題(Modulo MST)を解く

AtCoder_ABC328_E

AtCoder が提供しているABC(AtCoder Beginner Contest)328 のE問題をC++で解いてみました。ABC328は、2023年11月11日21:00に実施されました。

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

E問題 Modulo MST(Difficulty : 1160)

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

ABC328 E問題 Modulo MST

すべての辺から頂点数-1個の辺を取り出して調べます。AtCoder Problems による Difficulty は 1160 でした。

解答案

頂点数 $N$ の制約は、$2 \leqq N \leqq 8$ となっています。仮に $N=8$ の場合、グラフにあり得る辺は、$_8C_2 = 8 \times 7 / 2 = 28$ 個となります。グラフが全域木になるために辺の数は、$N-1=7$ 個である必要があります。このため、あり得る28個の辺から、7個の辺を選ぶ場合の数は、以下となります。

$$_{28}C_7 = \frac{28 \times 27 \times \cdots \times 22}{7 \times 6 \times \cdots \times 1} = 1184040 \doteqdot 10^6$$

選択した7個の辺でグラフ全体が連結している場合に、重さの総和の K による剰余を計算した後の最小値を求めます。

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

プログラムについてのコメントです。

  • m 個の辺から n-1 個の辺を取るための順列 p を用意します(20ー26行目)。
  • next_permutation を使って、n-1個の辺を取り出します。具体的には、p[i] の値が 0 の辺を取り出します。
  • 取り出した辺が以下を満たすか確認します。
    • 取り出した辺が新しい頂点とつないでいる(UnionFindで同じグラフに所属していないことを確認する)。ACL の UnionFind を使いました(2、4行目)。
  • n-1個の辺がすべて上記を満たせば、全体として連結しています。このとき、t_result の k による剰余を計算して、最小値 result を更新します(44、45行目)。
  • 最小値 result を出力する。

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

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

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

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

	vector<int> p(m);
	for (int i = 0; i < n - 1; ++i) {
		p[i] = 0;
	}
	for (int i = n - 1; i < m; ++i) {
		p[i] = 1;
	}

	ll result = INF;
	do {
		dsu uf(n + 1);
		ll t_result = 0;
		bool t_ok = true;
		for (int i = 0; i < m; ++i) {
			if (p[i] == 0) {
				if (!uf.same(u[i], v[i])) {
					uf.merge(u[i], v[i]);
					t_result += w[i];
				} else {
					t_ok = false;
					break;
				}
			}
		}
		if (t_ok) {
			result = min(result, t_result % k);
		}
	} while (next_permutation(p.begin(), p.end()));

	cout << result << endl;

	return 0;
}

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

Python の UnionFind と順列について

Python は、UnionFind を標準ライブラリとしては用意していません(私の実装はABC304E問題の解説記事で紹介しました)。また、順列生成(itertools.permutations)は同じ文字を区別しません(ABC326D問題でも解説しました)。

Python 版は、なるべく言語標準のライブラリで実装したいと考えています。このような事情で、この記事では、Python に移植したプログラムの紹介を省略します。

最後に

コンテストでは、全域木の定義を勘違いしていました。辺が $N-1$ 個より多い場合も探索範囲に入れていました。その結果、実装ができませんでした。※よく考えれば、辺が $N-1$ 個より多ければ、そもそも「木」になりません。

公式解説を読み理解して、自分なりに実装したプログラムを紹介しました。

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

COMMENT

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

CAPTCHA