YACS 2023年5月月赛 甲组 T3 铺砖问题 题解

题目链接

感谢 易敬然 大佬在 三铺地砖 中教会了我插头 DP,我看着他的题解慢慢写出了这题。

我们来考虑在某个位置放 12 砖块最远能放到哪里,如果我们向上放,即这样:

在叉叉的位置放一个 12 的砖块,当前的点是 (x,y),则最远可以放到上面的点即 (x1,y)

此时就能设出状态了,顺次填入砖块。考虑到点 (x,y) 时,假设上边左边即黄色区域的一定考虑过了,红色区域正在考虑,蓝色区域还没考虑。

f[i][j][k] 为考虑到 (i,j) 时,红色点从前往后的状态是 k,如果一位是 0,代表这位填过了或者本来不用填,是 1 代表需要填且必须填,最终答案显然为 f[n][n][0]

下面来看转移了,我们从上到下,每行按照从左到右依次转移。一共有三种(注意,我们不在红橙色区域外填,那样会填到蓝色区域里,我们将会在顺次考虑到蓝色区域时再算):

第一种:

需要注意的是,画叉叉的是要填的,必须要被填,值为 1

如果画叉叉的是 0,已经填过的或者是 "*" 那显然不能填,要特判,但橙色块一定是没填过的。

这种其实就是刚刚说过的,填完后顺次转移,橙色块(要考虑的块)向右移一个,可以转移到这种状态:

其中橙色方块左边的红色方块是 0,被填过了,剩下的就按照转移前的来。(使用的是贡献转移法)

第二种:

横着填,注意,标 "0" 的格子必须在状态中是 "0"。因为我们转移到下个状态时,它就成了黄色格子,是已经填完的了。

特判同一,填完变成:

第三种:

摆烂,啥都不填,但是标 0 的格子必须是 0,理由同二。

最后还会爆空间,滚动一下就行,时间复杂度:O(nm2m),代码:

复制代码
#include <iostream>
using namespace std;
int n, m, now;
int t, cnt;
int st[1 << 18], map[19][19];
int f[2][1 << 18];
const int mod = 1000000007;
char c;
int main () {
    cin >> n >> m;
    f[0][0] = 1;//都填完了,方案数为 1
    for (int i = 0; i <= n; i ++) for (int j = 0; j <= m; j ++) map[i][j] = 1;
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= m; j ++) {
            cin >> c;
            if (c == '*') map[i][j] = 0;
        }
    }
    for (int i = 1; i <= n; i ++) {
        for (int j = 1; j <= m; j ++) {
            cnt = 1;
            st[1] = 0;
            for (int k = m; k >= 1; k --) {//判断哪些能填哪些不能填。
                if ( (j > k && map[i][j - k]) || (j <= k && map[i - 1][j + m - k]) ) {
                    int sz = cnt;
                    for (int l = 1; l <= sz; l ++) {
                        st[l + sz] = st[l] << 1;
                        st[l] = st[l] << 1 | 1;
                    }
                    cnt *= 2;
                } else for (int l = 1; l <= cnt; l ++) st[l] <<= 1;
            }
            for (int k = 0; k < (1 << 18); k ++) f[(now + 1) & 1][k] = 0;
            for (int k = 1; k <= cnt; k ++) {//转移
                int state = st[k], pos;
                if (f[now & 1][state] == 0) continue;
                if (i != 1 && (state & (1 << m - 1) ) && map[i][j] != 0 && map[i - 1][j]) {//1 是没被填的,0 是填过不用填的
                    pos = state;//竖着填
                    pos ^= (1 << m - 1);
                    pos <<= 1;
                    f[(now + 1) & 1][pos] += f[now & 1][state];
                    if (f[(now + 1) & 1][pos] >= mod) f[(now + 1) & 1][pos] -= mod;
                }
                if (j != 1 && (state & (1 << m - 1) ) == 0 && (state & 1) && map[i][j] != 0 && map[i][j - 1]) {//横着填
                    pos = state;
                    -- pos;
                    pos <<= 1;
                    f[(now + 1) & 1][pos] += f[now & 1][state];
                    if (f[(now + 1) & 1][pos] >= mod) f[(now + 1) & 1][pos] -= mod;
                }
                if ( (state & (1 << m - 1) ) == 0) {
                    pos = state;
                    if (map[i][j] == 0) pos <<= 1;
                    else pos = pos << 1 | 1;
                    f[(now + 1) & 1][pos] += f[now & 1][state];
                    if (f[(now + 1) & 1][pos] >= mod) f[(now + 1) & 1][pos] -= mod;
                }
            }
            now ++;
        }
    }
    cout << f[now & 1][0];
    return 0;
}
复制代码

 

posted @   Xy_top  阅读(107)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示