【bzoj2331】[SCOI2011]地板 插头dp

插头dp
f[i][j][S]表示转移到第i行第j列轮廓线为S的方案数
0表示没有插头,1表示插头没有拐过弯,2表示插头拐过弯
1、
3进制不好写,写4进制
一共3^11个状态,200000左右
bit[i]=i*2
取出4进制的第i位  (S/(1<<bit[i-1]))%4
状态的第j位  1<<bit[j-1]
2、
bfs只记录有用的状态
3、
因为f[i][j]只与f[i][j-1]有关,所以不妨用滚动数组
4、

有下插头的位置正下方必然有上插头


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 200010
#define mod 20110520

using namespace std;

int a[110][110];
int f[2][maxn],tot[2],hash[2][maxn],head[50010],to[maxn],next[maxn],bit[110];
int n,m,now,pre,num;
char s[110];

void add(int s,int d)
{
	int x=s%50000;
	for (int p=head[x];p;p=next[p])
	  if (hash[now][to[p]]==s)
	  {
	  	f[now][to[p]]=(f[now][to[p]]+d)%mod;
	  	return;
	  }
	tot[now]++;
	hash[now][tot[now]]=s;
	f[now][tot[now]]=d;
	num++;to[num]=tot[now];next[num]=head[x];head[x]=num;
}

void dp()
{
	now=1;pre=0;
	tot[now]=1;
	hash[now][1]=0;
	f[now][1]=1;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=tot[now];j++) hash[now][j]<<=2;
		for (int j=1;j<=m;j++)
		{
			swap(now,pre);
			tot[now]=0;
			num=0;
			memset(f[now],0,sizeof(f[now]));
			memset(head,0,sizeof(head));
			for (int k=1;k<=tot[pre];k++)
			{
				int s=hash[pre][k],num=f[pre][k];
				if (!num) continue;
				int p=(s/(1<<bit[j-1]))%4,q=(s/(1<<bit[j]))%4;
				if (!a[i][j])
				{
					if (!p && !q) add(s,num);
				}
				else if (!p && !q)
				{
					if (a[i+1][j]) add(s+(1<<bit[j-1]),num);
					if (a[i][j+1]) add(s+(1<<bit[j]),num);
					if (a[i+1][j] && a[i][j+1]) add(s+(1<<bit[j-1]+1)+(1<<(bit[j]+1)),num);
				}
				else if (!p)
				{
					if (q==1)
					{
						s-=(1<<bit[j]);
						if (a[i][j+1]) add(s+(1<<bit[j]+1),num);
						if (a[i+1][j]) add(s+(1<<bit[j-1]),num);
					}
					else
					{
						s-=(1<<bit[j]+1);
						add(s,num);
						if (a[i+1][j]) add(s+(1<<bit[j-1]+1),num);
					}
				}
				else if (!q)
				{
					if (p==1)
					{
						s-=(1<<bit[j-1]);
						if (a[i][j+1]) add(s+(1<<bit[j]),num);
						if (a[i+1][j]) add(s+(1<<bit[j-1]+1),num);
					}
					else
					{
						s-=(1<<bit[j-1]+1);
						add(s,num);
						if (a[i][j+1]) add(s+(1<<bit[j]+1),num);
					}
				}
				else if (p==1 && q==1)
				{
					s-=(1<<bit[j-1])+(1<<bit[j]);
					add(s,num);
				}
			}
		}
	}
}

int main()
{
	for (int i=0;i<=100;i++) bit[i]=i*2;
	scanf("%d%d",&n,&m);
	if (n>=m)
	{
		for (int i=1;i<=n;i++)
		{
			scanf("%s",s+1);
			for (int j=1;j<=m;j++)
			  if (s[j]=='_') a[i][j]=1; else a[i][j]=0;
		}
	}
	else
	{
		for (int i=1;i<=n;i++)
		{
			scanf("%s",s+1);
			for (int j=1;j<=m;j++)
			  if (s[j]=='_') a[j][i]=1; else a[j][i]=0;
		}
		swap(n,m);
	}
	dp();
	printf("%d\n",f[now][1]);
	return 0;
}


你可能感兴趣的:(【bzoj2331】[SCOI2011]地板 插头dp)