【筆でリートコード60日】day25 バックトラッキングアルゴリズム - 216. 組み合わせ和Ⅲ、17. 電話番号の文字の組み合わせ

 


  トピック:

216. 合算和Ⅲ

n 合計が sum となる すべての数値の組み合わせを見つけk 、次の条件を満たします。

  • 1 ~ 9 の数字のみを使用してください
  • 各番号は 最大 1 回使用してください 

考えられるすべての有効な組み合わせのリストを返します  。リストには同じ組み合わせを 2 回含めることはできず、組み合わせは任意の順序で返されます。

例 1:

入力:  k = 3、n = 7
出力: [[1,2,4]]
説明: 
1 + 2 + 4 = 7
他に一致する組み合わせはありません。

例 2:

入力:  k = 3、n = 9
出力: [[1,2,6]、[1,3,5]、[2,3,4]]
説明:
 1 + 2 + 6 = 9 
1 + 3 + 5 = 9 
2 + 3 + 4 = 9
他に一致する組み合わせはありません。

例 3:

入力: k = 4、n = 1
出力: []
説明:有効な組み合わせが存在しません。
[1,9] の範囲内の 4 つの異なる数値を使用すると、取得できる最小の合計は 1+2+3+4 = 10 になります。10 > 1 であるため、有効な組み合わせは存在しません。

ヒント:

  • 2 <= k <= 9
  • 1 <= n <= 60

思考プロセスと知識ポイント: 

  • 再帰関数パラメータを決定する

77. 組み合わせ (新しいウィンドウが開きます)と同様に、修飾された結果を格納するには 1 次元配列のパスが必要であり、結果セットを格納するには 2 次元配列の結果が使用されます。

ここでもパスと結果をグローバル変数として定義します。

なぜパスという名前が付いているのでしょうか?上記のツリー構造から、結果は実際にはルート ノードからリーフ ノードへのパスであることがわかります。

次に、次のパラメータが必要です。

  • targetSum (int) ターゲットの合計。タイトルの n です。
  • k (int) は、タイトルに必要な k 個の数値のセットです。
  • sum (int) は、収集された要素の合計、つまりパス内の要素の合計です。
  • startIndex (int) は、次の for ループ検索の開始位置です。

したがって、コードは次のようになります。

vector<vector<int>> result;
vector<int> path;
void backtracking(int targetSum, int k, int sum, int startIndex)

実はここでもsumパラメータは省略可能で、その都度選択した要素の値からtargetSumを減算し、targetSumが0であれば条件を満たす結果が集まったと判断します。直感的に理解できるように、ここでも sum パラメーターを追加します。

また、バックトラッキング手法では、再帰関数のパラメータを一度に決定するのは難しいことにも注意しておく必要があり、通常はロジックを最初に記述し、どのようなパラメータが必要か、どのようなパラメータを入力するかが決まります。

  • 終了条件を決める

いつ終わるの?

上で述べたように、k 要素のみが取得され、ツリーが深くなっても意味がないため、k は実際にはツリーの深さを制限します。

したがって、 path.size() が k に等しい場合は、終了します。

このときパス上に集めた要素が(sum)とtargetSum(つまりタイトルに記載したn)と同じ場合、resultを使って現在の結果を集めます。

したがって、終了コードは次のようになります。

if (path.size() == k) {
    if (sum == targetSum) result.push_back(path);
    return; // 如果path.size() == k 但sum != targetSum 直接返回
}
  • 単層検索プロセス

処理の流れとしては、パスは木構造の辺に相当する毎回選択される要素を集め、パス内の要素の合計を合計カウントします。

コードは以下のように表示されます。

for (int i = startIndex; i <= 9; i++) {
    sum += i;
    path.push_back(i);
    backtracking(targetSum, k, sum, i + 1); // 注意i+1调整startIndex
    sum -= i; // 回溯
    path.pop_back(); // 回溯
}

処理プロセスと後戻りプロセスは 1 対 1 に対応しており、処理が増加すると後戻りが減算されることを忘れないでください。


  答え:

class Solution {
private:
    vector<vector<int>> result; // 存放结果集
    vector<int> path; // 符合条件的结果
    // targetSum:目标和,也就是题目中的n。
    // k:题目中要求k个数的集合。
    // sum:已经收集的元素的总和,也就是path里元素的总和。
    // startIndex:下一层for循环搜索的起始位置。
    void backtracking(int targetSum, int k, int sum, int startIndex) {
        if (path.size() == k) {
            if (sum == targetSum) result.push_back(path);
            return; // 如果path.size() == k 但sum != targetSum 直接返回
        }
        for (int i = startIndex; i <= 9; i++) {
            sum += i; // 处理
            path.push_back(i); // 处理
            backtracking(targetSum, k, sum, i + 1); // 注意i+1调整startIndex
            sum -= i; // 回溯
            path.pop_back(); // 回溯
        }
    }

public:
    vector<vector<int>> combinationSum3(int k, int n) {
        result.clear(); // 可以不加
        path.clear();   // 可以不加
        backtracking(n, k, 0, 1);
        return result;
    }
};

  トピック:

17. 電話番号のアルファベットの組み合わせ

数値のみを含む文字列を指定すると 2-9 、それが表すことができる文字の可能な組み合わせをすべて返します。回答は任意の順序で返すことができます  。

数字と文字のマッピングは次のようになります (電話キーの場合と同じ)。1 はどの文字にも対応しないことに注意してください。

例 1:

入力:数字 = "23"
出力: ["ad","ae","af","bd","be","bf","cd","ce","cf"]

例 2:

入力:数字 = ""
出力: []

例 3:

入力:数字 = "2"
出力: ["a","b","c"]

ヒント:

  • 0 <= digits.length <= 4
  • digits[i]['2', '9'] 範囲内の数値 です 。

 思考プロセスと知識ポイント: 

この質問を理解した後、次の 3 つの質問を解決する必要があります。

  1. 数字と文字のマッピング方法
  2. 2 文字の場合は 2 つの for ループ、3 文字の場合は 3 つの for ループ、というように繰り返していくと、コードがまったく書けないことがわかりました。
  3. 1 * #ボタンを入力するとその他の異常な状態になります

まず、リーフ ノードの結果を収集するには文字列 s が必要で、次に文字列配列の結果を使用して結果を保存しますが、私は引き続きこれら 2 つの変数をグローバルとして定義します。

パラメータをもう一度見てみると、パラメータの仕様はタイトルに示されている文字列数字であり、もう 1 つのパラメータは int 型のインデックスです。

終了条件は、インデックスが入力された桁数 (digits.size) と等しい場合です (桁を移動するために元のインデックスが使用されます)。

次に、結果を収集し、このレベルで再帰を終了します。


 答え:

class Solution {
private:
    const string letterMap[10] = {
        "", // 0
        "", // 1
        "abc", // 2
        "def", // 3
        "ghi", // 4
        "jkl", // 5
        "mno", // 6
        "pqrs", // 7
        "tuv", // 8
        "wxyz", // 9
    };
public:
    vector<string> result;
    string s;
    void backtracking(const string& digits, int index) {
        if (index == digits.size()) {
            result.push_back(s);
            return;
        }
        int digit = digits[index] - '0';        // 将index指向的数字转为int
        string letters = letterMap[digit];      // 取数字对应的字符集
        for (int i = 0; i < letters.size(); i++) {
            s.push_back(letters[i]);            // 处理
            backtracking(digits, index + 1);    // 递归,注意index+1,一下层要处理下一个数字了
            s.pop_back();                       // 回溯
        }
    }
    vector<string> letterCombinations(string digits) {
        s.clear();
        result.clear();
        if (digits.size() == 0) {
            return result;
        }
        backtracking(digits, 0);
        return result;
    }
};

「いいね!」、ブックマーク、コメントを歓迎します。あなたの励ましが私の創作の最大の動機です。(๑╹◡╹)ノ"""

著作権に関する声明: この記事は、CSDN ブロガー「Dumengjiu」によるオリジナルの記事であり、CC 4.0 BY-SA 著作権契約に準拠しています。転載する場合は、元のソースのリンクとこの声明を添付してください。
元のリンク: Dumengjiu の blog_CSDN blog-csdn ドメイン ブロガー

おすすめ

転載: blog.csdn.net/weixin_53310927/article/details/131355950