[データ構造とアルゴリズム] N x3グリッドグラフを着色するためのスキームの数を解くためのアルゴリズム

1.対象要件
  • nx 3グリッドがあります。各グリッドを赤、黄、緑の3色のいずれかで色付けし、隣接するグリッドの色が異なることを確認する必要があります(つまり、水平または垂直の側面が同じグリッド)。色)。
  • グリッドグラフの行数nを指定し、グリッドの配色の数を返します。答えは非常に大きい可能性があるため、残りの答えの結果を10 9 +7に返しください
  • 例1:
	输入:n = 1
	输出:12
	解释:总共有 12 种可行的方法

ここに画像の説明を挿入

  • 例2:
	输入:n = 2
	输出:54
  • 例3:
	输入:n = 3
	输出:246
  • 例4:
	输入:n = 7
	输出:106494
  • 促す:
    • n == grid.length
    • grid [i] .length == 3
    • 1 <= n <= 5000
2つ、ソリューションのアイデア
  • グリッドのサイズがi×3で、最後の行の色付け方法がtypeの場合、f [i] [type]を使用してスキームの数を表すことができます。
  • i番目の行が塗りつぶされると、その上の行(つまり、行i-1)のみが塗りつぶしスキームに影響するため、状態を表すためにf [i] [type]を使用するのが妥当です。これは「再帰」を使用するという考えです。
  • では、f [i] [type]を計算する方法は?
    • まず、タイプ自体が要件を満たしている必要があります。各行には3つのグリッドがあります。0、1、2がそれぞれ赤、黄、緑を表すために使用されている場合、タイプは3進数と見なすことができます。たとえば、type =(102)3の場合、 3つのグリッドが左から右に移動します色は黄色、赤、緑です。このようにして、要件を満たすすべてのタイプを前処理できます。具体的には、トリプルループを使用して各グリッドの色を個別に列挙でき、隣接するグリッドの色が異なる場合にのみ、タイプが要件を満たします。
    • 次に、f [i] [type]はすべてのf [i-1] [type ']の合計に等しくなければなりません。ここで、type'とtypeは隣接する行として使用できます。つまり、type 'とtypeの対応する位置を同じにすることはできません。
3つのアルゴリズム例
  • C ++の例
class Solution {
    
    
private:
    static constexpr int mod = 1000000007;

public:
    int numOfWays(int n) {
    
    
        // 预处理出所有满足条件的 type
        vector<int> types;
        for (int i = 0; i < 3; ++i) {
    
    
            for (int j = 0; j < 3; ++j) {
    
    
                for (int k = 0; k < 3; ++k) {
    
    
                    if (i != j && j != k) {
    
    
                        // 只要相邻的颜色不相同就行
                        // 将其以十进制的形式存储
                        types.push_back(i * 9 + j * 3 + k);
                    }
                }
            }
        }
        int type_cnt = types.size();
        // 预处理出所有可以作为相邻行的 type 对
        vector<vector<int>> related(type_cnt, vector<int>(type_cnt));
        for (int i = 0; i < type_cnt; ++i) {
    
    
            // 得到 types[i] 三个位置的颜色
            int x1 = types[i] / 9, x2 = types[i] / 3 % 3, x3 = types[i] % 3;
            for (int j = 0; j < type_cnt; ++j) {
    
    
                // 得到 types[j] 三个位置的颜色
                int y1 = types[j] / 9, y2 = types[j] / 3 % 3, y3 = types[j] % 3;
                // 对应位置不同色,才能作为相邻的行
                if (x1 != y1 && x2 != y2 && x3 != y3) {
    
    
                    related[i][j] = 1;
                }
            }
        }
        // 递推数组
        vector<vector<int>> f(n + 1, vector<int>(type_cnt));
        // 边界情况,第一行可以使用任何 type
        for (int i = 0; i < type_cnt; ++i) {
    
    
            f[1][i] = 1;
        }
        for (int i = 2; i <= n; ++i) {
    
    
            for (int j = 0; j < type_cnt; ++j) {
    
    
                for (int k = 0; k < type_cnt; ++k) {
    
    
                    // f[i][j] 等于所有 f[i - 1][k] 的和
                    // 其中 k 和 j 可以作为相邻的行
                    if (related[k][j]) {
    
    
                        f[i][j] += f[i - 1][k];
                        f[i][j] %= mod;
                    }
                }
            }
        }
        // 最终所有的 f[n][...] 之和即为答案
        int ans = 0;
        for (int i = 0; i < type_cnt; ++i) {
    
    
            ans += f[n][i];
            ans %= mod;
        }
        return ans;
    }
};
  • Pythonの例
class Solution:
    def numOfWays(self, n: int) -> int:
        mod = 10**9 + 7
        # 预处理出所有满足条件的 type
        types = list()
        for i in range(3):
            for j in range(3):
                for k in range(3):
                    if i != j and j != k:
                        # 只要相邻的颜色不相同就行
                        # 将其以十进制的形式存储
                        types.append(i * 9 + j * 3 + k)
        type_cnt = len(types)
        # 预处理出所有可以作为相邻行的 type 对
        related = [[0] * type_cnt for _ in range(type_cnt)]
        for i, ti in enumerate(types):
            # 得到 types[i] 三个位置的颜色
            x1, x2, x3 = ti // 9, ti // 3 % 3, ti % 3
            for j, tj in enumerate(types):
                # 得到 types[j] 三个位置的颜色
                y1, y2, y3 = tj // 9, tj // 3 % 3, tj % 3
                # 对应位置不同色,才能作为相邻的行
                if x1 != y1 and x2 != y2 and x3 != y3:
                    related[i][j] = 1
        # 递推数组
        f = [[0] * type_cnt for _ in range(n + 1)]
        # 边界情况,第一行可以使用任何 type
        f[1] = [1] * type_cnt
        for i in range(2, n + 1):
            for j in range(type_cnt):
                for k in range(type_cnt):
                    # f[i][j] 等于所有 f[i - 1][k] 的和
                    # 其中 k 和 j 可以作为相邻的行
                    if related[k][j]:
                        f[i][j] += f[i - 1][k]
                        f[i][j] %= mod
        # 最终所有的 f[n][...] 之和即为答案
        ans = sum(f[n]) % mod
        return ans
第四に、アルゴリズムの最適化
  • 数学について十分な知識がある場合は、上記の漸化式が線形であることがわかります。つまり、次のようになります。パスがあり、単純化できます。
  • 要件を満たすすべてのタイプを記述します。以下に示すように、合計12のタイプがあります。
	010, 012, 020, 021, 101, 102, 120, 121, 201, 202, 210, 212
  • それは2つのカテゴリーに分けることができます:
    • ABCカテゴリー:3色が異なり、012、021、102、120、201、210の合計6種類があります。
    • ABAタイプ:左右の色が同じで、010、020、101、121、202、212の6種類があります。
  • このようにして、12のタイプを2つに凝縮し、2つのタイプ間の漸化式を書き込もうとします。f [i] [0]を使用してABCタイプを示し、f [i] [1]を使用してABAタイプを示します。計算するときは、要件を満たす任意の色付け方法を行i-1に入れ、同じタイプの色付け方法が同等であるため、行iの計画の数を確認できます。
    • i-1行はABCタイプで、i行はABCタイプです。例として012を取り上げると、i番目の行は120または201になり、プランの数は2になります。
    • 行i-1はABCタイプ、行iはABAタイプです。例として012を取り上げると、i番目の行は101または121のみになり、プランの数は2になります。
    • i-1行はABAタイプで、i行はABCタイプです。例として010を使用すると、i番目の行は102または201のみになり、プランの数は2になります。
    • 行i-1はABAタイプ、行iはABAタイプです。例として010を使用すると、i番目の行は101、121、または202のみになり、プランの数は3になります。
  • したがって、漸化式は次のように取得できます。
    ここに画像の説明を挿入
  • C ++の例
class Solution {
    
    
private:
    static constexpr int mod = 1000000007;

public:
    int numOfWays(int n) {
    
    
        int fi0 = 6, fi1 = 6;
        for (int i = 2; i <= n; ++i) {
    
    
            int new_fi0 = (2LL * fi0 + 2LL * fi1) % mod;
            int new_fi1 = (2LL * fi0 + 3LL * fi1) % mod;
            fi0 = new_fi0;
            fi1 = new_fi1;
        }
        return (fi0 + fi1) % mod;
    }
};
  • 複雑さの分析
    • 時間計算量:O(N)。
    • スペースの複雑さ:O(1)。

おすすめ

転載: blog.csdn.net/Forever_wj/article/details/109346831