【BZOJ2331】[SCOI2011]地板 插头DP
【BZOJ2331】[SCOI2011]地板
Description
lxhgww的小名叫“小L”,这是因为他总是很喜欢L型的东西。小L家的客厅是一个的矩形,现在他想用L型的地板来铺满整个客厅,客厅里有些位置有柱子,不能铺地板。现在小L想知道,用L型的地板铺满整个客厅有多少种不同的方案?
需要注意的是,如下图所示,L型地板的两端长度可以任意变化,但不能长度为0。铺设完成后,客厅里面所有没有柱子的地方都必须铺上地板,但同一个地方不能被铺多次。
Input
输入的第一行包含两个整数,R和C,表示客厅的大小。
接着是R行,每行C个字符。’_’表示对应的位置是空的,必须铺地板;’*’表示对应的位置有柱子,不能铺地板。
Output
输出一行,包含一个整数,表示铺满整个客厅的方案数。由于这个数可能很大,只需输出它除以20110520的余数。
Sample Input
2 2
*_
__
*_
__
Sample Output
1
HINT
R*C<=100
题解:我们取R和C中小的那维做状态。显然状态是三维的:对于轮廓线上的每个位置,用0表示无插头,1表示有插头,并且这个L还没有拐弯,2表示有插头,并且L已经拐弯了。然后进行3*3的讨论吧!注意一个拐过弯的插头可以停止,一个没拐过弯的插头可以拐弯。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxn=177200; const int P=20110520; int n,m,k,tag,tot; char str[110][110]; int dp[2][maxn],bt[20],m3[maxn]; inline void upd(int a) {dp[k][a]+=tag; if(dp[k][a]>=P) dp[k][a]-=P;} int main() { scanf("%d%d",&n,&m); int i,j,S,T,p,q,x,y; for(i=1;i<=n;i++) scanf("%s",str[i]+1); if(n<m) { for(i=1;i<=m;i++) for(j=1;j<i;j++) swap(str[i][j],str[j][i]); swap(n,m); } for(i=bt[0]=1;i<=m+1;i++) bt[i]=bt[i-1]*3; for(tot=bt[m+1],i=1;i<tot;i++) m3[i]=i%3; dp[0][0]=1; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { k^=1; memset(dp[k],0,sizeof(dp[k])); for(S=0;S<tot;S++) if(dp[k^1][S]) { x=bt[j-1],y=bt[j],p=m3[S/x],q=m3[S/y],tag=dp[k^1][S],T=S-x*p-y*q; if(str[i][j]=='*') { if(!p&&!q) upd(T); continue; } if(!p&&!q) { if(i!=n&&j!=m) upd(T+((x+y)<<1)); if(i!=n) upd(T+x); if(j!=m) upd(T+y); } if(!p&&q==1) { if(i!=n) upd(T+x); if(j!=m) upd(T+(y<<1)); } if(!p&&q==2) { if(i!=n) upd(T+(x<<1)); upd(T); } if(!q&&p==1) { if(j!=m) upd(T+y); if(i!=n) upd(T+(x<<1)); } if(!q&&p==2) { if(j!=m) upd(T+(y<<1)); upd(T); } if(p==1&&q==1) upd(T); } } for(S=tot-1;S>=0;S--) dp[k][S]=(m3[S]>0)?0:dp[k][S/3]; } printf("%d",dp[k][0]); return 0; }//10 10 __________ __________ __________ __________ __________ __________ __________ __________ __________ __________
| 欢迎来原网站坐坐! >原文链接<