HDU 1693 Eat the Trees(多回路插头DP)

传送门

题目大意:给出一个n*m的有障碍的棋盘,问你有多少种回路恰好填满所有非障碍格子,其中回路可以不止一条。1<=n, m<=11。


思路

多回路插头DP,就是最简单插头DP了。只用判断每个格子的插头类型就行了,最小表示法也不需要。

我们记录轮廓线上插头的状态,那么对一个格子,分为三种情况:

①有左插头和上插头,相当于合并连通块。

②有左插头或上插头,相当于维持连通性。

③没有左插头且没有上插头,此时必须要有下插头和右插头,相当于新建连通块。

前提是知道回路的性质是每个点有两个度,所以有且只有两个插头。

再次强调维护的是轮廓线,由于这行最后一个格子转移完后和下一行第一个格子未转移前轮廓线一样,所以直接复制即可。(这里最后一个和第一个左边都不能有右插头)

由于我很懒,而且不喜欢代码长又长,所以第一次写就写了直接编码的LJ程序。

时间复杂度O(Tnm2^m)。


代码

#include <bits/stdc++.h>
#define maxn 12

using namespace std;

typedef long long LL;
int Case, T, n, m, a[maxn][maxn];
LL DP[maxn][maxn][1<<maxn];

int main(){

    scanf("%d", &T);

    while(T --){
        scanf("%d%d", &n, &m);

        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                scanf("%d", &a[i][j]);

        memset(DP, 0LL, sizeof(DP));
        DP[1][0][0] = 1LL;

        for(int i = 1; i <= n; i++){
            for(int j = 0; j < m; j++){
                for(int k = 0; k < (1<<(m+1)); k++){
                    int ConR = k & (1 << m);
                    int ConD = k & (1 << j);
                    int R = 1 << m, D = 1 << j;
                    if(!a[i][j+1]){
                        if(!ConR && !ConD)  DP[i][j+1][k] += DP[i][j][k];
                        continue;
                    }
                    if(ConR && ConD)  DP[i][j+1][k^R^D] += DP[i][j][k];
                    else if(ConR){ 
                        DP[i][j+1][k] += DP[i][j][k];
                        DP[i][j+1][k^R|D] += DP[i][j][k];
                    }
                    else if(ConD){
                        DP[i][j+1][k] += DP[i][j][k];
                        DP[i][j+1][k^D|R] += DP[i][j][k];
                    }
                    else  DP[i][j+1][k|R|D] += DP[i][j][k];
                }
            }
            if(i != n)  for(int k = 0; k < (1<<m); k++)  DP[i+1][0][k] = DP[i][m][k];
        }
        printf("Case %d: There are %I64d ways to eat the trees.\n", ++Case, DP[n][m][0]);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值