Time Limit: 3000MS | Memory Limit: 65536K |
Description

Expert as he was in this material, he saw at a glance that he'll need a computer to calculate the number of ways to fill the large rectangle whose dimensions were integer values, as well. Help him, so that his dream won't turn into a nightmare!
Input
Output

Sample Input
1 2 1 3 1 4 2 2 2 3 2 4 2 11 4 11 0 0
Sample Output
1 0 1 2 3 5 144 51205
这一题很做了好久。。。。。。。。
主要是一开始状态没设计好
我们先看这种情况
我们用0表示横着的和竖着的上半部分,用1表示竖着的下半部分
总而言之,如果为1,就表示是上一行的竖着的方块占用下来的
所以状态就出来了,用 f [ i ] [ S ] 表示到第 i 行的时候,状态为 S (如果有1,那就是从上一行竖着的下来的),并且前面 i-1 行已经填满的情况
所以转移也就出来了 f [ i ] [ S ] = ∑ f [ i - 1 ] [ st ]
最后的答案就是 f [ n + 1 ] [ 0 ]
剩下的就是判断 S 和 st 是否为可行状态了
看这个例子
st 0 1 0 0 S 1 1 1 0
我们前面已经说了,如果有1的话,那么必定是上一行下来的,所以上一行的这个位置就肯定是0!所有在第二个位置就不合法了,怎么快速判断呢?与运算~
既然当前行为1的话,上一行就必定为0,也就是不可能有两个1同时出现,所以 (S&st)==0 的话就表示满足了上述条件
剩下的就是看能否用横着的木块把剩下的填满了
那是不是直接判断 S 就行了呢?看下面的例子
st 1 0 0 0 S 0 0 0 0
如果判断 S 的话,显然可以用两个横着的填满,但我们仔细观察,虽然 S 可行,但是 st 就明显不可行了
如果 S 用两个横着的,st 用一个横着的,就还剩一个无法配对,而这一切都是因为 st 中有一个是从 st 的上一行传下来的,这就影响了 S 的判断,所以判断的时候还必须要同时考虑到 S 和 st !
怎么解决呢?把他们两个或起来就ok了,S|st=1000,这样就会返回false
再看个例子
st 0 0 0 S 1 0 0
S|st=100,就会返回true,一眼可以看出是对的
那这样会出错吗?比如下面的例子
st 1 0 0 S 0 0 0
我们我们如果不用或运算,直接看S,显然是无法用横着的填完的。。。。。
但事实也正并非如此,S 可以一个横着,然后一个竖着到下一行,也可以三个竖着到下一行
所以用或运算就简单多了,排除了不少麻烦 ------> S|st=100 ,会返回true
好了,到此也就完了,不懂得就看代码,才刚开始写这种题,代码不是很凝练,但是加了很多注释,思路很清晰
/*http://blog.youkuaiyun.com/jiangzh7
By Jiangzh*/
#include<cstdio>
#include<cstring>
typedef long long LL;
const int N=13;
int n,m,all;
LL f[N][1<<N];
void PT(int s)//_Debug用的,打印01序列
{
int a[N];
for(int i=m;i>0;i--)
{
a[i]=s&1;
s>>=1;
}
for(int i=1;i<=m;i++) printf("%d",a[i]);
puts("");
}
bool hash(int s)//判断剩下的0能否用横着的填满
{
for(int i=0;i<m;)
{
if((s&(1<<i))==0)
{
if(i==m-1) return false;//如果第m-1位为0,但是已经是最后一位,显然无法满足了
if ((s&(1<<(i+1)))!=0) return false;//当前为0,但是下一位不为0,显然不成立
i+=2;
}
else i++;
}
return true;
}
bool could(int s1,int s2)
{
bool flag1=(s1&s2)==0;//可行的话两个1必定不会重叠位置
bool flag2=hash(s1|s2);//看剩下的位置能否用横着的木板填满
return flag1&&flag2;
}
void work()
{
all=(1<<m)-1;
f[1][0]=1;
for(int i=1;i<=n;i++)
for(int S=0;S<=all;S++)
for(int st=0;st<=all;st++)
{
//PT(S);PT(st);
if(could(S,st))
{
//printf("%d\n",f[i+1][S]);
f[i+1][S]+=f[i][st];
}
}
printf("%lld\n",f[n+1][0]);//Linux下要用lld,Windows下用I64d,可以统一用cout的,难得改了
}
int main()
{
freopen("poj2411.in","r",stdin);
freopen("poj2411.out","w",stdout);
while(scanf("%d%d",&n,&m)==2 && n && m)
{
memset(f,0,sizeof(f));
work();
}
return 0;
}