http://www.lydsy.com/JudgeOnline/problem.php?id=2331
题面复制于洛谷
题目描述
lxhgww的小名叫”小L“,这是因为他总是很喜欢L型的东西。小L家的客厅是一个R*C的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板。现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案?需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0。
铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次。
输入输出格式
输入格式:
输入的第一行包含两个整数,R和C,表示客厅的大小。接着是R行,每行C个字符。'_'表示对应的位置是空的,必须铺地板;'*'表示对应的位置有柱子,不能铺地板。
输出格式:
输出一行,包含一个整数,表示铺满整个客厅的方案数。由于这个数可能很大,只需输出它除以20110520的余数。
输入输出样例
输入样例#1:2 2 *_ __
输出样例#1:1
输入样例#2:3 3 ___
_*_ ___输出样例#2:8
参考了:http://blog.csdn.net/regina8023/article/details/44838887
我终于可以告别插头dp啦233333……<—此人已疯
这道题的难点在于将插头dp的插头的定义进行修改。
0:无插头
1:有插头且当前格子所在的地板能再转弯。
2:有插头且当前格子所在的地板不能再转弯。
有了这些就可以按照插头dp的思想进行分情况讨论了:
(摘自参考博客)
1.00-->22 或 10 或 01
2.11-->00
3.10-->20 或 01
20-->00 或 02
4.01-->10 或 02
02-->00 或 20
最终把所有情况枚举累加即可。
PS:第二种情况的11转换成了00实质上是11相交的地方变成了这块地板的转折点(也可以理解为两块地板并在了一起)。
#include<algorithm> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int INF=2147483647; const int mod=300000; const int M=1000005; const int p=20110520; struct node{int to,nxt; }edge[M]; int head[M],cnt; int n,m; bool mp[105][105]; int cur,pre; int state[2][M]; ll ans[2][M],cntt; int tot[2]; int bit[15]; inline void getbit(){for(int i=1;i<15;i++)bit[i]=i<<1;return; } inline void add(int u,int v){cnt++;edge[cnt].to=v;edge[cnt].nxt=head[u];head[u]=cnt;return; } void insert(int now,ll num){int u=now%mod;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].to;if(state[cur][v]==now){ans[cur][v]+=num%p;ans[cur][v]%=p;return;}}add(u,++tot[cur]);state[cur][tot[cur]]=now;ans[cur][tot[cur]]=num%p;return; } void plugdp(){cur=0;tot[cur]=1;ans[cur][1]=1;state[cur][1]=0;for(int i=1;i<=n;i++){for(int j=1;j<=tot[cur];j++){state[cur][j]<<=2;}for(int j=1;j<=m;j++){memset(head,0,sizeof(head));cnt=0;pre=cur,cur^=1;tot[cur]=0;for(int k=1;k<=tot[pre];k++){int now=state[pre][k];ll num=ans[pre][k]%p;int is_down=(now>>bit[j-1])%4;int is_right=(now>>bit[j])%4;if(!mp[i][j]){if(!is_down&&!is_right)insert(now,num);}else if(!is_down&&!is_right){if(mp[i+1][j])insert(now+(1<<bit[j-1]),num);if(mp[i][j+1])insert(now+(1<<bit[j]),num);if(mp[i][j+1]&&mp[i+1][j])insert(now+2*(1<<bit[j-1])+2*(1<<bit[j]),num);}else if(is_down&&!is_right){if(is_down==1){if(mp[i+1][j])insert(now+(1<<bit[j-1]),num);if(mp[i][j+1])insert(now-(1<<bit[j-1])+(1<<bit[j]),num);}else{insert(now-2*(1<<bit[j-1]),num);if(mp[i][j+1])insert(now-2*(1<<bit[j-1])+2*(1<<bit[j]),num);}}else if(!is_down&&is_right){if(is_right==1){if(mp[i+1][j])insert(now+(1<<bit[j-1])-(1<<bit[j]),num);if(mp[i][j+1])insert(now+(1<<bit[j]),num);}else{insert(now-2*(1<<bit[j]),num);if(mp[i+1][j])insert(now+2*(1<<bit[j-1])-2*(1<<bit[j]),num);}}else if(is_down==1&&is_right==1)insert(now-(1<<bit[j-1])-(1<<bit[j]),num);}}}for(int k=1;k<=tot[cur];k++)cntt+=ans[cur][k];return; } int main(){getbit();scanf("%d%d",&n,&m);if(n<m){swap(n,m);for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){char ch=getchar();while(ch!='*'&&ch!='_')ch=getchar();if(ch=='_')mp[j][i]=1;}}}else{for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){char ch=getchar();while(ch!='*'&&ch!='_')ch=getchar();if(ch=='_')mp[i][j]=1;}}}plugdp();printf("%lld\n",cntt);return 0; }