sgu131 Hardwood floor 状压DP

先贴个连接:http://www.nocow.cn/index.php/Sgu/131  参考了NOCOW的题解...

题意是要铺满N*M的区域,现在有1*2和2*2挖掉一个角 (都允许旋转)两种地砖,问共有多少种铺法..

每次转移共有六种方案

/*
##   #.   ##   ##   #.  .#
..   #.   #.   .#   ##  ##
1    2    3    4    5   6
*/

由于最大只有9*9,每行可以压缩一下来存储状态,并且第i行的状态不会影响i+2行以后的状态,所以可以用f[i]表示当前行达到状态i的方案数,g[i]表示当前行铺满时,对下一行产生的影响为i的方案数,然后两个数组来回滚几次地推一下,就ok了。答案很大,注意用long long。

/*
##   #.   ##   ##   #.  .#
..   #.   #.   .#   ##  ##
1    2    3    4    5   6
*/
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
#define bin(i) 1<<i
#define emp(a,i) (!(a & bin(i)))
using namespace std;
typedef long long ll;
const int maxn=(1<<11);
int n,m;
int full;
ll g[maxn],f[maxn];
void dfs(int a,int b,ll kk)
{
    if (a==full)
    {
        g[b]+=kk;
        return ;
    }
    for(int i=0; i<m; i++)
    {
        if (emp(a,i))
        {
            if (i+1<m && emp(a,i+1))
            {
                dfs(a|bin(i)|bin(i+1),b,kk); // #1
                if (emp(b,i)) dfs(a|bin(i)|bin(i+1),b|bin(i),kk); // #3
                if (emp(b,i+1)) dfs(a|bin(i)|bin(i+1),b|bin(i+1),kk); //#4
            }
            if (emp(b,i))
            {
                dfs(a|bin(i),b|bin(i),kk); // #2
                if (i+1<m && emp(b,i+1))
                {
                    dfs(a|bin(i),b|bin(i)|bin(i+1),kk); // #5
                }
                if (i>0 && emp(b,i-1))
                {
                    dfs(a|bin(i),b|bin(i)|bin(i-1),kk); // #6
                }
            }
            break;
        }
    }
}
int main()
{
//    freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    full=(1<<m)-1;
    memset(f,0,sizeof f);
    memset(g,0,sizeof g);
    f[full]=1;
    for (int k=0; k<=n; k++)
    {
        for (int i=0; i<=full; i++)
        if (f[i]) dfs(i,0,f[i]);
        memcpy(f,g,sizeof g);
        memset(g,0,sizeof g);
    }
    cout<<f[0]<<endl;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值