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

Posted Xy_top

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了YACS 2023年5月月赛 甲组 T3 铺砖问题 题解相关的知识,希望对你有一定的参考价值。

题目链接

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

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

在叉叉的位置放一个 $1 * 2$ 的砖块,当前的点是 $(x, y)$,则最远可以放到上面的点即 $(x - 1, y)$。

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

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

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

第一种:

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

这种其实就是刚刚说过的,填完后可以转移到这种状态:

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

第二种:

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

填完变成:

第三种:

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

最后特判一下填砖块的位置不是 "*" 即可,还会爆空间,滚动一下就行。

时间复杂度:$O(nm\\cdot m^2)$,代码:

#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;

 

UVA1224 LA3904 Tile Code铺砖问题

The city of Songpa is now carrying out a project to build a bicycle transportation system called green Songpa. By the end of this year, citizens and visitors alike will be able to pick up and drop off bicycles throughout the city. Recently, it was decided to attach a number tag to each bicycle for management use. The bicycles will be under control of the citys traffic system.
    The number tag contains a tile code of length n, which consists of 1×2, 2×1, and 2×2 tiles placed on a 2 × n rectangular plate in a way that every cell of the plate is covered by exactly one tile. The plate is divided into 2n cells of size 1×1. Of course, no two tiles are allowed to overlap each other. The 2 × 5 plate and a tile code of length 5 are shown in Figures 1 and 2, respectively. The code will always be read from left to right. However, there is no distinction between the top side and the bottom side of the code. The code may be turned upside down. The code shown in Figure 3 is essentially the same
code as in Figure 2.
在这里插入图片描述

    Given a positive integer n, the project director Dr. Yang wants to know how many tile codes of length n there are, that is, the number of ways to place the three kinds of tiles into a 2 × n rectangular plate subject to the above conditions. Write a program that can help him.
Input
Your program is to read from standard input. The input consists of T test cases. The number of test cases T is given in the first line of the input. Each test case is given in a single line, which contains a positive integer n, 3 ≤ n ≤ 30.
Output
Your program is to write to standard output. Print exactly one line for each test case. The line should contain the number of tile codes of length n.
Sample Input
2
3
4
Sample Output
3
8

问题链接UVA1224 LA3904 Tile Code
问题简述:(略)
问题分析:数学的铺砖问题,需要找出递推式再进行数学计算。
程序说明:(略)
参考链接:(略)
题记:(略)

AC的C++语言程序如下:

/* UVA1224 LA3904 Tile Code */

#include <bits/stdc++.h>

using namespace std;

const int N = 30;
int f[N + 1];

int main()
{
    f[0] = 0;
    f[1] = 1;
    for (int i = 2; i <= N; i++)
        f[i] = f[i - 1] + 2 * f[i - 2];

    int t, n;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        int k = f[n];
        if (n % 2 == 0) {
            k += f[n / 2 + 1];
            if (n / 2 % 2) k++;
        } else {
            k += f[n / 2];
            if (n / 2 % 2) k--;
        }
        printf("%d\\n", k);
    }

    return 0;
}

以上是关于YACS 2023年5月月赛 甲组 T3 铺砖问题 题解的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4883[Lydsy2017年5月月赛]棋盘上的守卫 KM算法

bzoj4886 [Lydsy2017年5月月赛]叠塔游戏

BZOJ 4884 [Lydsy2017年5月月赛]太空猫(单调DP)

[bzoj4883][Lydsy2017年5月月赛]棋盘上的守卫

bzoj4881 [ Lydsy2017年5月月赛 ] -- 二分图染色+线段树

BZOJ 4883 [Lydsy2017年5月月赛]棋盘上的守卫(最小生成环套树森林)