T73528 插头DP

有一个n*m的网格,每个格子可以向4个方向延伸出插头(边缘的格子不能向外延伸),每个格子每个方向上最多有1个插头,也可以没有。与同一条边相邻的两个格子在这个方向上的插头状态必须相同。

现在已知一部分格子的插头数目(0~4),也有一些格子的插头数目是未确定的(用-1表示)。

对于一种将所有未确定格子的插头数目确定下来的方案t,此时每个格子具体的插头方向仍不唯一,不妨将合法方案数记做ft。求 ∑ f t 2 \sum f_t^2 ft2

关于这个 f t 2 f_t^2 ft2,相当于从 ft 种方案中选出有序的两种(可重复)的方案数。于是我们同时对两个相同的网格进行 DP,要求 -1 的格子枚举的度数是相同的。复杂度 O ( n m 4 m ) O(nm4^m) O(nm4m)

插头 DP 特别要注意特判每行开头结束的时候的转移。

#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
const int mod=10007;
int f[2][1<<8][1<<8],a[100][100],st1[10],st2[10];
int read()
{
	int x=0,flag=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-') flag=-1;c=getchar();}
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return x*flag;
}
inline void add(int &a,int b) {a+b>=mod?a+=b-mod:a+=b;}
int main()
{
	int T=read();
	while(T--)
	{
		memset(f,0,sizeof(f));
		int n=read(),m=read(),now=0,U=(1<<m+1)-1,dd=U&(U<<2);
		for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]=read();
		f[now][0][0]=1;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++,now^=1,memset(f[now^1],0,sizeof(f[now^1])))
				for(int s1=0;s1<=U;s1++)
					for(int s2=0;s2<=U;s2++)
						for(int k=0;k<5;k++) if(a[i][j]==k||a[i][j]==-1)
						{
							int ss=s1&1,tt=s1>>m&1,nn=k-ss-tt,tp=f[now][s1][s2],top1=0,top2=0;
							if(nn<0||nn>2||!tp) continue;
							if(nn==0) st1[++top1]=s1<<1&dd;
							else if(nn==1) 
							{
								if(j<m) st1[++top1]=s1<<1&dd|1;
								st1[++top1]=s1<<1&dd|2;
							}
							else if(j<m) st1[++top1]=s1<<1&dd|3;
							
							ss=s2&1,tt=s2>>m&1,nn=k-ss-tt;
							if(nn<0||nn>2||!tp) continue;
							if(nn==0) st2[++top2]=s2<<1&dd;
							else if(nn==1) 
							{
								if(j<m) st2[++top2]=s2<<1&dd|1;
								st2[++top2]=s2<<1&dd|2;
							}
							else if(j<m) st2[++top2]=s2<<1&dd|3;
							
							for(int e=1;e<=top1;e++)
								for(int z=1;z<=top2;z++) add(f[now^1][st1[e]][st2[z]],tp);
						}
						
		cout<<f[now][0][0]<<'\n';
	}
	return 0;
}
/*by DT_Kang*/
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值