传送门
题目大意:给出一个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;
}