BZOJ2331: [SCOI2011]地板

190 篇文章 2 订阅
84 篇文章 2 订阅

题面

题意

给出一张地图,请用“L”形地板覆盖所有非障碍的格子,“L”可以旋转,但两边的长度都必须大于0。

做法

因为数据范围非常小,所以可以用插头dp。
对于插头我们记录三种状态:
0:没有插头。
1:有插头,且是未拐过弯的“L”。
2:有插头,且是拐过弯的“L”。
然后在转移时分3*3=9种情况分别转移,有点耐心就好了。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#define ll long long
#define N 110
#define M 20110520
using namespace std;

ll m,n,num,dp[2][200100],san[N],ans,li,lj;
char str[N];
bool mm[N][N],now,cur;
vector<ll>have[2];

inline ll get(ll u,ll v){return u/san[v-1]%3;}
inline ll chg(ll u,ll v,ll w){return u+(w-get(u,v))*san[v-1];}
inline void in(ll u)
{
	if(!dp[now][u]) have[now].push_back(u);
	dp[now][u]+=num;
	if(dp[now][u]>M) dp[now][u]-=M;
}

int main()
{
	ll i,j,k,t,p,q,kk;
	cin>>m>>n;
	for(i=1;i<=m;i++)
	{
		scanf("%s",str+1);
		for(j=1;j<=n;j++)
		{
			if(m<n) mm[j][i]=(str[j]=='*');
			else mm[i][j]=(str[j]=='*');
		}
	}
	if(m<n) swap(m,n);
	for(i=1;i<=m;i++) mm[i][0]=mm[i][n+1]=1;
	for(i=0;i<=n+1;i++) mm[0][i]=mm[m+1][i]=1;
	now=san[0]=num=1;
	for(i=1;i<=n+1;i++) san[i]=san[i-1]*3;
	in(0);
	for(i=1;i<=m;i++)
	{
		for(j=1;j<=n;j++)
		{
			swap(now,cur);
			have[now].clear();
			for(kk=0;kk<have[cur].size();kk++)
			{
				k=have[cur][kk];
				p=get(k,j),q=get(k,j+1);
				t=chg(k,j,0),t=chg(t,j+1,0);
				num=dp[cur][k];
				if(mm[i][j]) in(k);
				else if(!p&&!q)
				{
					if(!mm[i][j+1]) in(chg(k,j+1,1));
					if(!mm[i+1][j]) in(chg(k,j,1));
					if(!mm[i][j+1]&&!mm[i+1][j])
					{
						t=chg(k,j,2),t=chg(t,j+1,2);
						in(t);
					}
				}
				else if(!p&&q==1)
				{
					if(!mm[i+1][j]) in(chg(t,j,1));
					if(!mm[i][j+1]) in(chg(k,j+1,2));
				}
				else if(!p&&q==2)
				{
					in(t);
					if(!mm[i+1][j]) in(chg(t,j,2));
				}
				else if(p==1&&!q)
				{
					if(!mm[i+1][j]) in(chg(k,j,2));
					if(!mm[i][j+1]) in(chg(t,j+1,1));
				}
				else if(p==1&&q==1) in(t);
				else if(p==2&&!q)
				{
					in(t);
					if(!mm[i][j+1]) in(chg(t,j+1,2));
				}
				dp[cur][k]=0;
			}
		}
		swap(now,cur);
		have[now].clear();
		for(kk=0;kk<have[cur].size();kk++)
		{
			k=have[cur][kk];
			num=dp[cur][k];
			in(k*3);
			dp[cur][k]=0;
		}
	}
	cout<<dp[now][have[now][0]];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值