P5074 Eat the Trees

Eat the Trees

Luogu P5074

题目背景

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;
}
posted @ 2022-08-11 15:52  Hanx16Msgr  阅读(27)  评论(0编辑  收藏  举报