HDU 5713 状压dp

题目大意:给出n个点和m条边,求出图中恰好有k个连通图的方案数。

首先我们求出每个选择状态的连边方案数num,为了使枚举时不重复,

每次去掉最后一位lowbit( i ),只用包含这个点的连边情况转移。

然后求出保证这个状态只有1个联通块的方案数f,枚举所有状态

利用去掉最后一位lowbit( i )之后的状态s,枚举s的子集j,

i^j就代表了原状态i中不含j状态的情况,剩余的j状态的连边共有2的num[j]次幂种

利用f[i^j]这个原状态内部只有一个联通块的方案数 * 剩余的j状态的连边方案总数

就是在i状态中利用f[i^j]求出的不满足的方案数(因为这样就大于1个联通块了)。

dp数组代表i状态有j个联通块的情况数。

最后枚举状态,和状态的子集,即dp[i][q] = dp[ j ][q-1] * f[i - j ] (也可以写成i^j)

最后输出满状态的情况。

下面代码

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define mode 1000000009
using namespace std;
typedef long long ll;
ll num[(1<<16)+5];
ll f[(1<<16)+5];
ll dp[(1<<16)+5][16];
ll v[205];
int dis[16][16];
int lowbit(int x)
{
    return x & -x;
}
void init()
{
    memset(num,0,sizeof(num));
    memset(f,0,sizeof(f));
    memset(dp,0,sizeof(dp));
    memset(dis,0,sizeof(dis));
}
int n,m,k;
int gett(int x)
{
    int flag = 0;
    int cnt = 0;
    for(int i = 1;i <= n;i++)
    {
        if((1<<(i-1))&x)
        {
            flag = i;
            break;
        }
    }
    for(int i = 1;i <= n;i++)
    {
        if((1<<(i-1))&x)
        {
            if(dis[flag][i])cnt++;
        }
    }
    return cnt;
}
int main()
{
    int T;
    v[0] = 1;
    scanf("%d", &T);
    for(int i = 1;i <= 200;i++)
    {
        v[i] = v[i-1]*2%mode;
    }
    for(int w = 1;w <= T;w++)
    {
        scanf("%d%d%d", &n, &m, &k);
        init();
        for(int i = 1;i <= m;i++)
        {
            int a,b;
            scanf("%d%d", &a, &b);
            dis[a][b] = dis[b][a] = 1;
        }
        for(int i = 1;i <= (1<<n)-1;i++)
        {
            num[i] = num[i^lowbit(i)]+gett(i);
        }
        for(int i = 1;i <= n;i++)
        {
            f[(1<<(i-1))] = 1;
        }

        for(int i = 1;i <= (1<<n)-1;i++)
        {
            int s = i^lowbit(i);
            ll tt = 0;
            for(int j = s;j;(--j)&= s)
            {
                tt += f[j^i]*v[num[j]]%mode;
                tt %= mode;
            }
            f[i] = ((v[num[i]]-tt)%mode+mode)%mode;
        }
        if(k==1)
        {
            printf("Case #%d:\n%lld\n",w,f[(1<<n)-1]);
            continue;
        }
        for(int i = 0;i <= (1<<n)-1;i++)
        {
            dp[i][1] = f[i];
        }
        for(int i = 1;i <= (1<<n)-1;i++)
        {
            int s = i^lowbit(i);
            for(int j = s;j;(--j)&= s)
            {
                for(int q = 2;q <= k;q++)
                {
                    dp[i][q] += dp[j][q-1]*f[i^j]%mode;
                    dp[i][q] %= mode;
                }
            }
        }
        printf("Case #%d:\n%lld\n",w,dp[(1<<n)-1][k]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zzk_233/article/details/81812574