【Leetcode】1411. Number of Ways to Paint N × 3 Grid

题目地址:

https://leetcode.com/problems/number-of-ways-to-paint-n-3-grid/

考虑对一个 n × 3 n\times 3 n×3的网格,每个格子可以涂 3 3 3种颜色,要求相邻的两个格子不能涂同一种颜色。问涂色总共的方案数。答案模 1 0 9 + 7 10^9+7 109+7返回。

以下用 0 , 1 , 2 0,1,2 0,1,2三个数字来指代三种颜色。第一行有 12 12 12种方案,其中只含两种颜色的方案有 6 6 6种,分别是 010 , 020 , 101 , 121 , 202 , 212 010,020,101,121,202,212 010,020,101,121,202,212,含 3 3 3种颜色的方案也是 6 6 6种,分别是 012 , 021 , 102 , 120 , 201 , 210 012,021,102,120,201,210 012,021,102,120,201,210。设 f [ k ] [ 0 ] f[k][0] f[k][0]是第 k k k行如果只涂两种颜色一共有多少合法方案, f [ k ] [ 1 ] f[k][1] f[k][1]是第 k k k行如果涂三种颜色一共有多少合法方案。考虑第 k − 1 k-1 k1行,如果其涂两种颜色,例如 010 010 010,那么第 k k k行涂两种颜色的方案有 101 , 121 , 202 101,121,202 101,121,202一共 3 3 3种,第 k k k行图三种颜色的方案有 102 , 201 102,201 102,201一共 2 2 2种;如果其涂三种颜色,例如 012 012 012,那么第 k k k行涂两种颜色的方案有 101 , 121 101,121 101,121一共 2 2 2种,第 k k k行涂三种颜色的方案有 120 , 201 120,201 120,201一共 2 2 2种。注意到由于对称性,涂色方案只与涂了的颜色种数有关,与具体涂了什么颜色无关,所以 { f [ k ] [ 0 ] = 3 f [ k − 1 ] [ 0 ] + 2 f [ k − 1 ] [ 1 ] f [ k ] [ 1 ] = 2 f [ k − 1 ] [ 0 ] + 2 f [ k − 1 ] [ 1 ] \begin{cases}f[k][0]=3f[k-1][0]+2f[k-1][1]\\f[k][1]=2f[k-1][0]+2f[k-1][1] \end{cases} {f[k][0]=3f[k1][0]+2f[k1][1]f[k][1]=2f[k1][0]+2f[k1][1]只需要递推一遍最后返回 f [ n ] [ 0 ] + f [ n ] [ 1 ] f[n][0]+f[n][1] f[n][0]+f[n][1]即可。初始条件 f [ 1 ] [ 0 ] = f [ 1 ] [ 1 ] = 6 f[1][0]=f[1][1]=6 f[1][0]=f[1][1]=6。代码如下:

public class Solution {
    public int numOfWays(int n) {
        int MOD = (int) (1e9 + 7);
        long[][] f = new long[2][2];
        f[1][0] = f[1][1] = 6;
        for (int i = 2; i <= n; i++) {
            f[i & 1][0] = (f[i - 1 & 1][0] * 3 + f[i - 1 & 1][1] * 2) % MOD;
            f[i & 1][1] = (f[i - 1 & 1][0] * 2 + f[i - 1 & 1][1] * 2) % MOD;
        }
        
        return (int) ((f[n & 1][0] + f[n & 1][1]) % MOD);
    }
}

时间复杂度 O ( n ) O(n) O(n),空间 O ( 1 ) O(1) O(1)

可以用矩阵乘方 + 快速幂来做。若 A = [ 3 2 2 2 ] A=\begin{bmatrix} 3&2\\2&2 \end{bmatrix} A=[3222],实际上就是要求 v = A n − 1 [ 6 6 ] v=A^{n-1}\begin{bmatrix} 6\\6 \end{bmatrix} v=An1[66],最终返回 v 0 + v 1 v_0+v_1 v0+v1。算矩阵乘方可以用快速幂来做。代码如下:

public class Solution {
    
    class Matrix {
        private final int MOD = (int) (1e9 + 7);
        private long a11, a12, a21, a22;
        
        public Matrix(int a11, int a12, int a21, int a22) {
            this.a11 = a11;
            this.a12 = a12;
            this.a21 = a21;
            this.a22 = a22;
        }
        
        public void mult(Matrix B) {
            long c11 = (a11 * B.a11 + a12 * B.a21) % MOD;
            long c12 = (a11 * B.a12 + a12 * B.a22) % MOD;
            long c21 = (a21 * B.a11 + a22 * B.a21) % MOD;
            long c22 = (a21 * B.a12 + a22 * B.a22) % MOD;
            a11 = c11;
            a12 = c12;
            a21 = c21;
            a22 = c22;
        }
    }
    
    public int numOfWays(int n) {
        int MOD = (int) (1e9 + 7);
        Matrix res = new Matrix(1, 0, 0, 1), A = new Matrix(3, 2, 2, 2);
        n--;
        // 快速幂模板
        while (n > 0) {
            if ((n & 1) == 1) {
                res.mult(A);
            }
            
            n >>= 1;
            A.mult(A);
        }
        
        return (int) ((res.a11 + res.a12 + res.a21 + res.a22) * 6 % MOD);
    }
}

时间复杂度 O ( log ⁡ n ) O(\log n) O(logn),空间 O ( 1 ) O(1) O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值