AtCoder

ABC278 F問題(Shiritori)を解く

AtCoder_ABC278_F

AtCoder が提供しているABC(AtCoder Beginner Contest)278 のF問題をC++とPythonで解いてみました。ABC278は、2022年11月19日21:00に実施されました。

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

F問題 Shiritori(Difficulty : 1452)

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

ABC278 F問題 Shiritori

bitDPを使ってゲーム問題を解きます。AtCoder Problems による Difficulty は、1452 でした。

解答案

DPを使います。dp[i][c] は、残っている文字列の集合 i、しりとりで使われた最後の文字 c(この文字が、次に選ぶ最初の文字列の最初の文字になっている必要があります)のときに、勝つ文字列を選べる場合に 1、どの文字列を選んでも負けるもしくは文字列が選べない場合に 0 とします。集合 i は、整数と考えて2進数の下位 j bit目が 1 であれば、文字列 Sj は使われず残っているとします。

残っている文字列の集合 i と、次に選ぶ文字列 j、文字 c は、以下を満たす必要があります。

  • 集合 i の下位から j bit目が 1 になっている(文字列 j が使われずに残っている)。
  • 文字 c と文字列 j の最初の文字が一致している。

この場合、dp[i から j を抜いた集合][文字列 j の最後の文字] が相手の負け(0)になっていれば、文字列 j を選べば勝つことができます。逆にどの文字列を選んでも勝てなければ、負けとなります。また、文字列が選べない場合も負けになります。

集合 i は、値が少ない方から調べていけばよいです(bitDP の考え方です)。初期値は、以下となります。

dp[0][c]=0、c はすべての文字

選べる文字列が無ければ、その時点で負けになるためです。

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

プログラムを補足します。

  • 文字列は、最初の文字と最後の文字が分かればよいので、それを first_char、last_char として格納します。
  • 配列 dp の初期値を定めます(18ー20行目)。
  • 上で考察したように dp を更新します。
    • すべての j に対して、集合 i の下位から j bit目が 1 になっていて、文字 c と文字列 j の最初の文字が一致していることを確認します。
    • dp[i から j を抜いた集合][文字列 j の最後の文字] が相手の負け(0)になっていれば、dp[i][c] は勝ち(1)になります。

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

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

int main()
{
	int n;
	cin >> n;
	vector<int> first_char(n);
	vector<int> last_char(n);
	for (int i = 0; i < n; ++i) {
		string s;
		cin >> s;
		first_char[i] = s[0] - 'a';
		last_char[i] = s[s.length() - 1] - 'a';
	}

	vector<vector<int>> dp(1 << n, vector<int>(26, -1));
	for (int c = 0; c < 26; ++c) {
		dp[0][c] = 0;
	}
	for (int i = 1; i < (1 << n); ++i) {
		for (int c = 0; c < 26; ++c) {
			int w_or_l = 0;
			for (int j = 0; j < n; ++j) {
				if (((((1 << j) & i) != 0)&&(first_char[j] == c)) 
				  &&(dp[i ^ (1 << j)][last_char[j]] == 0)) {
					w_or_l = 1;
				}
			}
			dp[i][c] = w_or_l;
		}
	}

	for (int c = 0; c < 26; ++c) {
		if (dp[(1 << n) - 1][c] == 1) {
			cout << "First" << endl;
			return 0;			
		}
	}
	cout << "Second" << endl;

	return 0;
}

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

Python プログラム例(ABC278F)

Python 版も基本的な考え方は同じです。

以下のプログラムは、「Python (CPython 3.11.4)」では、TLE判定となり、「Python (PyPy 3.10-v7.3.12)」で、AC判定となりました。

"""AtCoder Beginner Contest 278 F"""
n = int(input())
first_char = [0] * n
last_char = [0] * n
for i in range(n):
    s = input()
    first_char[i] = ord(s[0]) - ord('a')
    last_char[i] = ord(s[-1]) - ord('a')

dp = [[-1 for _ in range(26)] for _ in range(1 << n)]
for c in range(26):
    dp[0][c] = 0
for i in range(1, 1 << n):
    for c in range(26):
        w_or_l = 0
        for j in range(n):
            if (i & (1 << j)) != 0 and first_char[j] == c and \
               dp[i ^ (1 << j)][last_char[j]] == 0:
                w_or_l = 1
        dp[i][c] = w_or_l

result = False
for c in range(26):
    if dp[(1 << n) - 1][c] == 1:
        result = True
print("First" if result else "Second")

こちらも「AC」と判定されました。

最後に

ABC354E問題解説記事)と同じ構造の問題でした。理解を深めるために、この問題を記事にしました。

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

COMMENT

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

CAPTCHA