NC51189 Mondriaan‘s Dream

这篇博客介绍了如何使用两种不同大小的砖块(1*2和2*1)在n*m矩阵中进行无空隙且无重叠的密铺,通过分析砖块组合的规律和递推方法,探讨了两种不同的解题思路,并提供了相应的C++代码实现。核心在于理解砖块放置的规则和状态转移技巧。
摘要由CSDN通过智能技术生成

NC51189 Mondriaan’s Dream

题意:

n * m的矩阵,用1 * 2和2 * 1的砖快密铺,问多少种方法:

题解:

方法1:
我们现在规定砖头的竖放的上部分为1,砖头的横放或者是竖放的下部分为0
在这里插入图片描述
我们每两层进行分析,分析01的关系
我们设上层为k,下层为j
因为竖砖上为1下为0,所以上下两层&的结果必然都是0,j&k = = 0,
且j|k中连续的0均为偶数,因为竖砖和横砖的组合只有两种情况如下图:
蓝色字为两行或的结果,或的结果中0只能是连续偶数个出现,不可能连续奇数个出现
在这里插入图片描述
方法二:
我们用01表示这个地方放不放砖
第i行只与第i-1行相关
枚举第i-1行的每个状态,就可以推出第i行的状态
如果第i-1行的某处没放,那么第i行这个位置必须放竖砖,所以对上一行状态按位取反之后的1的位置就是放置了竖方块的状态。
然后枚举这行其他点的位置,记录答案
这个思路很常规,但也很妙

代码:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

int n, m;
int const N = 1e4 + 10;
LL f[20][N];
int st[N];
vector<int> state[N];

int main() {
    while (cin >> n >> m && n && m) {
        // 初始化
        memset(f, 0, sizeof f);
        f[0][0] = 1;
        for (int i = 0; i < 1 << n; ++i) state[i].clear();

        // 预处理st
        for (int i = 0; i < 1 << n; i ++ ) {
            int cnt = 0;
            st[i] = true;
            for (int j = 0; j < n; j ++ )
                if (i >> j & 1) {//如果第j位是1 ,统计之前连续0的数量奇偶性 
                    if (cnt & 1) st[i] = false;
                    cnt = 0;
                }
                else cnt ++ ;//如果是第j位是0,记录连续0的数量 
            if (cnt & 1) st[i] = false;
        }

        // 预处理state
        for (int i = 0; i < 1 << n; ++i) {
            for (int j = 0; j < 1 << n; ++j) {
                if ((i & j) == 0 && st[i | j]) state[i].push_back(j);
            }
        }

        // dp转移
        for (int i = 1; i <= m; ++i) {
            for (int j = 0; j < 1 << n; ++j) {
                for (int k = 0; k < state[j].size(); ++k) {  // 获得所有的合法方案
                    f[i][j] += f[i - 1][state[j][k]];
                }
            }
        }
        printf("%lld\n", f[m][0]);
    }
    
    return 0;
}
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
int n,m;
ll add;
ll dp[2][1<<12];
void dfs(int i,int s,int cur)
{
   if(cur==m) {dp[i][s]+=add;return;}
   dfs(i,s,cur+1);
   if(cur<m-1 && !(s&(1<<cur)) && !(s&1<<(cur+1)) )
   dfs(i,s|(1<<cur)|1<<(cur+1),cur+2);
}
int main()
{
    while(scanf("%d%d",&n,&m),n+m)
    {
        if(n*m%2) {printf("0\n");continue;}
        int rt=(1<<m)-1;
        add=1;
        memset(dp,0,sizeof(dp));
        dfs(0,0,0);
        for(int i=1;i<n;i++)
            for(int j=0;j<=rt;j++)
				if(dp[(i-1)%2][j])
				{
					add=dp[(i-1)%2][j];
					dfs(i%2,~j&rt,0);
				} 
                    
        printf("%I64d\n",dp[(n-1)%2][rt]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值