ドミノのラインナップ(UCFローカルプログラミングコンテスト2016 I、収圧DP)

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;
}

 

おすすめ

転載: blog.csdn.net/The___Flash/article/details/105167253