Permutation (HDU - 3811,状压 DP)

1. Title link:

HDU-3811

2. The main idea of ​​the topic:

Two integers n, m in the first line.

In the next m lines, each line contains two integers ai and bi, indicating that the number ai is bi.

Find the number of permutations from 1 to n that satisfy at least one of the restrictions in m.

3. Analysis:

The most violent approach is to enumerate the restrictions that should be met by binary enumeration, and use the idea of ​​tolerance to calculate, but this is definitely TLE...

Consider the opposite event of the answer: the arrangement of 1~n does not satisfy any of the restrictions in m.

Note that dp[i][j] means that the number of i is placed in the arrangement, and the use state of the number is the number of schemes that do not meet any of the restrictions in m.

See the code for details.

4. Code implementation:

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

typedef long long ll;

const int M = (int)17;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)1e9 + 7;

ll fac[M + 5];
int one[1<<M];
bool ban[M][M];
ll dp[2][1<<M];

int cal(int n)
{
    int cnt = 0;
    while(n)
    {
        cnt += (n & 1);
        n >>= 1;
    }
    return cnt;
}

int main()
{
    fac[0] = 1; for(int i = 1; i <= 17; ++i)    fac[i] = fac[i - 1] * i;
    for(int i = 0; i < (1<<17); ++i)    one[i] = cal(i);
    int T; scanf("%d", &T);
    for(int ca = 1; ca <= T; ++ca)
    {
        memset(ban, 0, sizeof(ban));
        int n, m; scanf("%d %d", &n, &m);
        for(int i = 0, a, b; i < m; ++i)    scanf("%d %d", &a, &b), ban[--a][--b] = 1;
        for(int i = 0; i < n; ++i)  dp[0 & 1][1<<i] = !ban[0][i];
        for(int i = 1; i < n; ++i)
        {
            for(int j = 0; j < (1<<n); ++j)
            {
                if(one[j] != i + 1) continue;
                dp[i & 1][j] = 0;
                for(int k = 0; k < n; ++k)
                {
                    if(!(j & (1<<k)) || ban[i][k])  continue;
                    dp[i & 1][j] += dp[i - 1 & 1][j^(1<<k)];
                }
            }
        }
        printf("Case %d: %lld\n", ca, fac[n] - dp[n - 1 & 1][(1<<n) - 1]);
    }
    return 0;
}

 

Guess you like

Origin blog.csdn.net/The___Flash/article/details/105458440