[LeetCode 周赛184] 4. 给 N x 3 网格图涂色的方案数(递推、状压dp、数学、巧妙解法)

1. 题目来源

链接:5383. 给 N x 3 网格图涂色的方案数

2. 题目说明

在这里插入图片描述
在这里插入图片描述

3. 题目解析

方法一:递推+状压dp+巧妙解法

就为啥我还能把样例 1 的图示看成 3 列呢,结果就把题意理解错了…纠结了好久…真吐了。

主要思想为递推、状压 dp,下面简单理下思路:

  • 状态定义:dp[i][bits] 已经填色到第 i 行,i 行填色方案为 bits 的方案数。在此要注意 bits 为 6 位二进制,通过简单位运算即可将其切割为三个 [0,4),即用 01 位,23 位, 45 位表示三个格子的 3 种颜色情况,并将数字 3 废弃,利用 0,1,2 表示这三种颜色
  • 首先对第一行进行初始化,由于是 6 位二进制,那么就会产生 64 种可能填色的情况,遍历这 64 种情况即可。判断时有两个条件需要满足:3 不在颜色种类内,并且相邻颜色不能相等。满足这两个条件,则将第一行 bits 填色方案结果初始化为 1
  • 然后递推计算剩下的所有行即可,当前行也会产生 64 种情况,遍历计算。在此需要注意,若当前行的某种填色方案已经合法了,就需要判断是否与上一行的填色方案产生了冲突,即判断同列值是否相等
  • 并且值得注意的是,若前一行的填色方案不冲突但是填色方案为 0 时,就直接 continue,寻找下一个填色方案。为什么呢?在此有两种情况需要考虑
    • 该方案不合法,所有无法通过之前的递推进行转移
    • 取模后变成 0,不会对结果产生影响,直接 continue 即可
  • 再将当前行的某种填色方案与前一行的填色方案状态进行累加即可

即这个状态压缩递推就是:

  • 先枚举行
  • 再枚举该行的所有填色方案
  • 该行当前的填色方案是否合法
  • 若合法则检查前一行是否会与当前行的填色方案是否产生冲突
  • 最后累加方案数即可

参见代码如下:

// 执行用时 :392 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :8.4 MB, 在所有 C++ 提交中击败了100.00%的用户

#define LL long long
const LL MOD = 1e9+7;
LL dp[5050][65];

class Solution {
public:
    int get(int v, int c){
        return (v >> (c * 2)) % 4;
    }

    int numOfWays(int n) {
        for (int s = 0; s < 64; s++){
            int a = get(s, 0), b = get(s, 1), c = get(s, 2);
            dp[1][s] = 0;
            if (a == 3 || b == 3 || c == 3) continue;
            if (a == b || b == c) continue;
            dp[1][s] = 1;
        }

        for (int i = 2; i <= n; i++) {
            for (int cur = 0; cur < 64; cur++) {
                dp[i][cur] = 0;
                int na = get(cur, 0), nb = get(cur, 1), nc = get(cur, 2);
                if (na == 3 || nb == 3 || nc == 3) continue;
                if (na == nb || nb == nc) continue; 
                for (int prev = 0; prev < 64; prev++) {
                    int pa = get(prev, 0), pb = get(prev, 1), pc = get(prev, 2);
                    if (pa == na || pb == nb || pc == nc) continue;
                    if (dp[i - 1][prev] == 0) continue;
                    dp[i][cur] = (dp[i][cur] + dp[i - 1][prev] ) % MOD;
                }
            }
        }
        
        LL ans = 0;
        for (int s = 0; s < 64; s++){
            ans = (ans + dp[n][s]) % MOD;
        }
        return ans;
    }
};

注释版。

参见代码如下:

#define LL long long
const LL MOD = 1e9+7;
LL dp[5050][65];
// dp[i][bits] 已经填色到第i行,i行填色方案为bits的方案数
// bits为6位二进制,将其切割为三个[0,4),将3废弃,利用0,1,2表示三种颜色

class Solution {
public:
    int get(int v, int c){
        return (v >> (c * 2)) % 4;
    }

    int numOfWays(int n) {
        // 初始化i=1第一行
        for (int s = 0; s < 64; s++){
            int a = get(s, 0), b = get(s, 1), c = get(s, 2);
            dp[1][s] = 0;
            // 3不在颜色种类内
            if (a == 3 || b == 3 || c == 3) continue;
            // 相邻的颜色不能相等
            if (a == b || b == c) continue;
            dp[1][s] = 1;
        }
        // 递推计算剩下的所有行
        for (int i = 2; i <= n; i++) {
            for (int cur = 0; cur < 64; cur++) {
                // 当前行填cur方法
                dp[i][cur] = 0;
                int na = get(cur, 0), nb = get(cur, 1), nc = get(cur, 2);
                if (na == 3 || nb == 3 || nc == 3) continue;
                if (na == nb || nb == nc) continue; 
                // 当前行填cur合法,查看上一行颜色是否有冲突
                for (int prev = 0; prev < 64; prev++) {
                    int pa = get(prev, 0), pb = get(prev, 1), pc = get(prev, 2);
                    if (pa == na || pb == nb || pc == nc) continue;
                    // dp数组在i-1行方案为prev时方案数为0即不存在这种方案,在此有两种情况导致:
                    // 1. prev该方案不合法,所有无法通过之前的递推进行转移
                    // 2. 取模后变成0,不会产生影响,直接continue即可
                    if (dp[i - 1][prev] == 0) continue;
                    dp[i][cur] = (dp[i][cur] + dp[i - 1][prev] ) % MOD;
                }
            }
        }
        
        LL ans = 0;
        for (int s = 0; s < 64; s++){
            ans = (ans + dp[n][s]) % MOD;
        }
        return ans;
    }
};

方法二:数学+巧妙解法

来自题解区 1 号大佬,tql数学解决非常快乐

在此我就抛 link 了不再多阐述什么了。

对于本题还可以进行矩阵乘法的优化,能将时间复杂度降低到 O ( n l o g n ) O(nlogn) ,但也是太菜了,先挖个坑,待填。

发布了410 篇原创文章 · 获赞 381 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/yl_puyu/article/details/105479787