题意:给出一个n*m的地图,要用闭合回路经过每一个点(可以是多条),有一些格子是障碍格不能经过,问有多少种方案
分析:连通性状态压缩DP的模板题,首先引入“插头”和“轮廓线”的概念:
插头:表示与周围某个方向的格子有路径(如图红线)
轮廓线:分割已经决策过和未决策过的格子的分割线(如图蓝线,黄色是转移之前)
dp[i][j][S]表示当前枚举到第i行第j列(如图i=3,j=2)并且插头情况为S(用二进制表示,如图S={1,1,1,0,1}=29)
状态转移也很简单:
(1)如果转移后格子的插头经过目前分界线(如图黄线)的个数为2,即下一个状态这个格子不会再有插头,直接加当前方案数即可。
(2)如果转移后格子的插头经过目前分界线的个数为1,即下一个状态这个格子会有且仅有一个插头,(会有2种情况,分别是插头在下,插头在右,图示为插头在下,每种情况都要加)
(3)如果转移后格子的插头经过目前分界线的个数为0,即下一个状态这个格子一定会有2个插头,直接加即可。
障碍格只需要在转移时判断一下,如果是情况(1)或(2),即把目标赋值为0,如果是情况(3),则没有影响直接转移
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
#include<cstdlib>
#include<queue>
#include<cmath>
#include<bitset>
#define SF scanf
#define PF printf
#define MAXN 15
#define MAXM 4200
using namespace std;
long long c,dp[MAXN][MAXN][MAXM],n,m,mat[MAXN][MAXN];
void work(){
dp[0][m][0]=1;
for(int i=1;i<=n;i++){
for(int k=0;k<(1<<m);k++)
dp[i][0][k<<1]=dp[i-1][m][k];
for(int j=1;j<=m;j++)
for(int k=0;k<(1<<(m+1));k++){
int p=1<<j;
int q=p>>1;
bool x=k&p;
bool y=k&q;
if(mat[i][j]){
dp[i][j][k]=dp[i][j-1][k^q^p];
if(x!=y)
dp[i][j][k]+=dp[i][j-1][k];
}
else{
if(x==0&&y==0)
dp[i][j][k]=dp[i][j-1][k];
else
dp[i][j][k]=0;
}
}
}
}
int main(){
int t;
SF("%d",&t);
for(int num=1;num<=t;num++){
SF("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
SF("%d",&mat[i][j]);
memset(dp,0,sizeof dp);
work();
PF("Case %d: There are %lld ways to eat the trees.\n",num,dp[n][m][0]);
}
}