F - Shiritori Editorial by en_translator
This problem can be solved with a DP (Dynamic Programming) parametrized by pairs of “the set of unused words” and “the last character.” Consider a DP table such that
- \(\operatorname{dp}[S][c]\coloneqq\) \(1\) if you can win from the state where the set of unused words is \(S\subseteq\{s _ 1,\ldots,s _ N\}\) and the last character is \(c\) ; and \(0\) if you cannot.
Then, you can determine the values of the DP table as follows:
\[\operatorname{dp}[S][c]=\left\{\!\!\begin{array}{cll}1&&({}^\exists s\in S.\;s[0]=c\wedge\operatorname{dp}[S\setminus\{s\}][s[-1]]=0)\\0&&(\text{otherwise}).\end{array}\right. \]
Here, \(s[0]\) and \(s[-1]\) denotes the first and last character of \(s\), respectively. The transition of DP reflects the fact that “you will win the game if there is a word such that if you say that word, the opponent will lose.”
If there is \(c\) such that \(\operatorname{dp}[\{s_1,\ldots,s_N\}][c]\), then Takahashi can win.
In order to fill this DP table correctly, you need to fill all \(\operatorname{dp}[S\setminus\{s\}][s[-1]]\) prior to finding \(\operatorname{dp}[S][c]\). It is achieved by a typical trick of bit DP: convert \(S\) to a \(N\)-bit integer and process them in their ascending order.
Let \(\sigma\) be the size of alphabet. Since there are \(2^N\sigma\) states, and filling an element of a DP table costs \(O(N)\) time per state, so the overall time complexity is \(O(2^NN\sigma)\).
A sample code follows. We assume that \(\sigma\lt w\), where \(w\) is the word size.
#include <iostream>
#include <vector>
#include <utility>
#include <string>
int main() {
using namespace std;
unsigned N;
cin >> N;
// the first and last character of the i-th string
vector<pair<unsigned char, unsigned char>> info(N);
for (auto&&[first, last] : info) {
string S;
cin >> S;
first = S.front() - 'a';
last = S.back() - 'a';
}
// the c-th significant bit of dp[S] = whether the first player wins when unused words are S and the last character is c
vector<unsigned> dp(1UL << N);
for (unsigned S{1}; S < 1UL << N; ++S)
for (unsigned c{}; c < N; ++c)
if (1 & S >> c)
dp[S] |= (1UL & ~dp[S ^ 1UL << c] >> info[c].second) << info[c].first;
// if any bit of dp[2^N - 1] is set, then the first player wins
cout << (dp.back() ? "First" : "Second") << endl;
return 0;
}
posted:
last update: