P5074 Eat the Trees
Eat the Trees
题目背景
HDU1693:Eat the Trees
题目描述
给出n*m的方格,有些格子不能铺线,其它格子必须铺,可以形成多个闭合回路。问有多少种铺法?
输入格式
每个测试点多组数据
第一行一个正整数T,表示有T组数据
每组数据:
第1行,n,m(2<=n,m<=12)
从第2行到第n+1行,每行m个数字(1 or 0),1表铺线,0表不铺线
输出格式
每组数据输出一个整数(表方案数)
样例 #1
样例输入 #1
2
6 3
1 1 1
1 0 1
1 1 1
1 1 1
1 0 1
1 1 1
2 4
1 1 1 1
1 1 1 1
样例输出 #1
3
2
Solution
算是个插头 DP 的基础题了,甚至比洛谷上插头 DP 的模板题还要简单。
假设一个 DP 数组 \(f[i][j][s]\) 表示当前枚举到了 \((i,j)\) 的格子,并且当前轮廓线的状态为 \(s\),那么按照插头 DP 模板的思路,先获取当前格子左侧插头 \(isR=s>>(j-1)\) 和上侧插头 \(isD=s>>j\),进行分类讨论:
-
\((i,j)\) 是障碍物
这种情况很简单,只需要左侧插头和上侧插头均不存在就是合法的,即当 \(isD=isR=0\) 时,\(f[i][j][s]+=f[i][j-1][s]\)。
-
\(isD=isR=0\) 左插和上插都不存在
因为形成回路,所以每个格子都具有两个插头,因此当前格子一定具有下插和右插,所以就是 \(f[i][j][s+(1<<j-1)+(1<<j)]+=f[i][j-1][s]\) (加入这两个新的插头)。
-
\(isR=1,isD=0\) 仅存在左插
既然有左插,那么当前格子就可以右插和下插二选一,判断一下右插和下插会不会插到障碍物上然后转移即可:\(f[i][j][s]+=f[i][j-1][s],f[i][j][s-(1<<j-1)+(1<<j)]+=f[i][j][s]\)。
-
\(isR=0,isD=1\) 仅存在上插
与仅存在左插的情况相同,\(f[i][j][s]+=f[i][j-1][s],f[i][j][s-(1<<j)+(1<<j-1)]+=f[i][j-1][s]\)。
-
\(isR=isD=1\) 左插和上插同时存在
既然一个格子具有两个插头,那这两个插头就是这个格子全部的合法插头了,所以这个格子不会再拥有新的插头,所以有 \(f[i][j][s-(1<<j)-(1<<j-1)]+=f[i][j-1][s]\)。
到达每一行的第一个的时候需要将轮廓线给滚到下一行,具体做法就是 \(f[i][0][j<<1]=f[i-1][m][j]\),这样就可以做到把插头全部右移一位了。
Code
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
#define bit(a,b) (a>>b-1&1) //表示a二进制上第b位的数字
using namespace std;
template<typename T> void read(T &k)
{
k=0;T flag=1;char b=getchar();
while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
while (isdigit(b)) {k=k*10+b-48;b=getchar();}
k*=flag;
}
bool mp[15][15];
int n,m,T,maxn;
const int _SIZE=1<<14;
long long f[15][15][_SIZE+5];
int main()
{
read(T);
while (T--)
{
read(n),read(m);
maxn=1<<(m+1);mem(f,0);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) read(mp[i][j]);
mem(f,0);
f[0][m][0]=1;
for (int i=1;i<=n;i++)
{
for (int j=0;j<maxn;j++) f[i][0][j<<1]=f[i-1][m][j];
for (int j=1;j<=m;j++)
{
for (int k=0;k<maxn;k++)
{
int isR=bit(k,j),isD=bit(k,j+1),nowsta=k;
long long nowans=f[i][j-1][k];
if (!mp[i][j])
{
if ((!isD) && (!isR)) f[i][j][nowsta]+=nowans;
}
else if ((!isD) && (!isR))
f[i][j][nowsta+(1<<j-1)+(1<<j)]+=nowans;
else if ((!isR) && isD)
{
if (mp[i][j+1]) f[i][j][nowsta]+=nowans;
if (mp[i+1][j]) f[i][j][nowsta-(1<<j)+(1<<j-1)]+=nowans;
}
else if (isR && (!isD))
{
if (mp[i+1][j]) f[i][j][nowsta]+=nowans;
if (mp[i][j+1]) f[i][j][nowsta-(1<<j-1)+(1<<j)]+=nowans;
}
else
f[i][j][nowsta-(1<<j-1)-(1<<j)]+=nowans;
}
}
}
printf("%lld\n",f[n][m][0]);
}
return 0;
}