1.タイトルリンク:
ドミノのラインナップ
2.トピックの主なアイデア:
Tグループの例
各グループには整数n(n <= 16)があります。これは、n個の両面ドミノがあることを意味します。各ドミノの前面には番号sがあり、背面には番号tがあります。
n個のドミノは、隣接するドミノの隣接する側の番号が同じである場合にのみ、一列に並べることができます(任意の数のドミノの前面と背面で反転できます)。
これらのn個のドミノを連続して配置できる方法をいくつ尋ねます(2つのドミノの順序が異なる場合のみ、配置が異なります)。
3.分析:
dp [i] [j] [k]は、n個のドミノの使用状態がi、最後のドミノがj、最後のドミノが反転されていない(0)/反転されていない(1)ことを意味します。
状態を設計した後、状態遷移方程式を書くのは簡単ですが、それが反転されているかどうかに関係なく、スキームはシーケンスとのみ異なることに注意する必要があります。
最終回答の統計にも小さな穴があります。
当然、最後のドミノiを列挙するだけです。
if s [i] == t [i]:ans + = dp [(1 << n)-1] [i] [0]
else ans + = dp [(1 << n)-1] [i] [0] + dp [(1 << n)-1] [i] [1]。
つまり、最後のドミノの表と裏の数字が違うと、違う配置に対応しているに違いないと最初は思いました。
実際、1つの状況が欠けています。
以下は私の考えの過程です(証拠ではなく、非常に大まかに書かれています!)
現在のシーケンスが(a1、a2)(a2、a3)(a3、a4)...(an-1、an)(an、an + 1)、a2!= a3、a3!= a4、..であるとします。 、an-1!= an、!= a + 1。
最後のドミノを反転した後のシーケンスは(a1、a2)(a2、a3)(a3、a4)...(an-2、an-1)(an-1、an)(an + 1、an)
2つのシーケンスが合法であり、異なると仮定しますが、これは明らかに隣接する(an-1、an)(an + 1、an)と矛盾しています。
合法性を満たすために、(an-1、an)を反転することしかできず、シーケンスは(a1、a2)(a2、a3)(a3、a4)...(an-2、an-1)(an、 an-1)(an + 1、an)
それらの中で(an-2、an-1)(an、an-1)は隣接していて矛盾しているので、再帰的に反転すると、得られるシーケンスは次のようになります。
(a2、a1)(a3、a2)(a4、a3)...(an-1、an-2)(an、an-1)(an + 1、an)
このシーケンスは合法であり、元のシーケンスとは異なると想定しているため、描画するのは難しくありません。a1= a3 = a5 = ...; a2 = a4 = a6 = ...;
つまり、これらのn個のドミノはペアで同じです。
最後のドミノの表と裏の番号が異なる場合、n個のドミノがペアで同じである場合、ドミノは反転後も同じシーケンスに対応し、そうでない場合は異なるシーケンスに対応する必要があると推測できます。
要約すると、すべてのドミノが同じであると判断する必要があります。答えはnです。
4.コードの実装:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = (int)1e5;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;
int s[16][2];
ll fac[17];
ll dp[1<<16][16][2];
bool check(int n)
{
for(int i = 1; i < n; ++i) if(!(s[0][0] == s[i][0] && s[0][1] == s[i][1] || s[0][0] == s[i][1] && s[0][1] == s[i][0])) return 0;
return 1;
}
int main()
{
fac[0] = 1; for(int i = 1; i <= 16; ++i) fac[i] = fac[i - 1] * i % mod;
int T; scanf("%d", &T);
while(T--)
{
int n; scanf("%d", &n);
for(int i = 0; i < n; ++i) scanf("%d %d", &s[i][0], &s[i][1]);
if(check(n)) {printf("%lld\n", fac[n]); continue;}
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; ++i) dp[1<<i][i][0] = dp[1<<i][i][1] = 1;
for(int i = 3; i < (1<<n); ++i)
{
for(int j = 0; j < n; ++j)
{
if(!(i & (1<<j))) continue;
int state = (i ^ (1<<j));
for(int k = 0; k < n; ++k)
{
if(!(state & (1<<k))) continue;
if(s[k][0] == s[j][0]) dp[i][j][0] = (dp[i][j][0] + dp[state][k][1]) % mod;
else if(s[k][1] == s[j][0]) dp[i][j][0] = (dp[i][j][0] + dp[state][k][0]) % mod;
if(s[k][0] == s[j][1]) dp[i][j][1] = (dp[i][j][1] + dp[state][k][1]) % mod;
else if(s[k][1] == s[j][1]) dp[i][j][1] = (dp[i][j][1] + dp[state][k][0]) % mod;
}
}
}
ll cnt = 0;
for(int i = 0; i < n; ++i) {cnt = (cnt + dp[(1<<n) - 1][i][0]) % mod; if(s[i][0] != s[i][1]) cnt = (cnt + dp[(1<<n) - 1][i][1]) % mod;}
printf("%lld\n", cnt);
}
return 0;
}