Javaのインタビュー - 動的計画法と組み合わせの数

最近のブラシ摩耗力の件名で、にブラシ65本の異なるパスは、大学で、この質問hihocoderに磨くことをしていたが、現在はほぼ完全に忘れ去られ、おそらく動的なプログラミングの知識の今私たちが戻って見てみましょう。

タイトルから言えば

元のタイトルは以下のとおりです。

左コーナーMXNグリッドに位置ロボットは(以下、「スタート」と表示されたポイントのフィギュアを開始します)。

ロボットは右または下に一歩を移動することができます。(「完了」と表示され、次の図の)グリッドの右下隅に到達しようとしているロボット。

Q.合計でありますどのように多くの異なるパス?

例えば、マップは7×3のグリッドです。パスはどのように多くの可能性がありますか?

説明:M及びnの値100以下です。

例1:

入力:M = 3、N = 2

出力:3

説明:

左上、右下に3つのパスの合計から。

  1. 右 - >右 - >ダウン
  1. 右 - >下 - >右
  1. ダウン - >右 - >右

例2:

入力:M = 7、N = 3

出力:28

フォワード思考

あなたが出発点であるときは、一番下の行または右端のいずれかでない限り、あなたは、右または下に、2つの選択肢がありますについて考えるために、通常のアイデアに従ってみましょう、そしてあなたは、このような下のようなオプションを1つだけ(持っています行では、あなただけ右に回すことができる)、他の場所は、次の2つの選択肢があります。

したがって、この考え方によると、私たちはコードを書くことができます。

class Solution {
    public int uniquePaths(int m, int n) {
        // 特殊情况:起点即终点
        if (m == 1 && n == 1) {
            return 1;
        }
        // 当前处于(1,1),终点为(m,n)
        return walk(1, 1, m, n);
    }
    
    public int walk(int x, int y, int m, int n){
        // 已经处于终点
        if (x >= m && y >= n) {
            return 0;
        }
        // 处于最下面一排或者最右边一列
        if (x >= m || y >= n) {
            return 1;
        }
        // 往下走,有多少种走法
        int down = walk(x, y + 1, m, n);
        // 往右走,有多少种走法
        int right = walk(x + 1, y, m, n);
        // 从当前(x,y)出发,走到(m,n),共有多少种走法
        return down + right;
    }
}

最適化

私たちは、最適化することができない場所がありません、このようなアプローチについて考えます。

あなたはそれを見つけることになりますwalk第1の判定方法if (x >= m && y >= n)決してとして、true次の決定があるため、if (x >= m || y >= n)戻り値は直接達成することは不可能だった、すでに臨界点であるx >= m && y >= n状況を。このように、裁判官を除去することができます。

我々ビューの(1,1)点からの位置、終点が到達(2,2)、この中間点次いで、(3,3)であると仮定し、その後いくつかの移動は、それはありますか?その後、二つの第一の種類、(1,2)、次いで、(2,2)に、または、第(2,1)と(2,2)です。

私たちは、エンド(3,3)に(2,2)から、上に書いた場合は、この考え方自体は正しいですが、このような状況が最適化されるべきであるため、我々は、二回カウントされます。(1,1)からのパスのわずか6種類の合計、(3,3)にあるため、その後で、二つの経路が重複しているがあった成長し、より多くの中間点は、繰り返しパスは、より多くなります。mn

これがある前面的选择ために后面的选择も、影響を受けることが后面的选择同じですが、前面的选择異なる、また、さまざまなオプションであると考えられます。

もちろん、后面的选择我々は戻っての最初の選択をした場合、よりユニークな、そしてあなたは、ダブルカウントの数を減らすことができます。したがって、我々は逆の考え方を試すことができます。

リバース思考

我々は出発点から始まるが、後方端から始まりにされていない場合は、カウントを開始しました。のみ(2,3)によって到達することができると仮定エンドポイント(3,3)、(3,2)を直接、(2、3)のみ(2,2)及び(1,3)に直接によって、 (3)直接(1,2)、(1,2)のみ(1,1)によって到達可能に到達することができ、直接ので、(1,3)のみ直接(1,1)によります。

、開始点(1,1)のみから直接外に、点の一番左の列と一番上の行に加えて、他の点(x、y)が形成されている(X-1、Y)と(:我々はルールを描くことができ二つの点に直接Y-1×)。

したがって、この考え方によると、私たちはコードを書くことができます。

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] result = new int[m][n];
        int j;
        for (int i = 0; i < m; i++) {
            for (j = 0; j < n; j++) {
                if (i == 0 || j == 0) {
                    // 最上面一排的点和最左边一列的点,只能由(1,1)到达
                    result[i][j] = 1;
                } else {
                    // 其他的点都可以由左边的点和上面的点到达
                    result[i][j] = result[i - 1][j] + result[i][j - 1];
                }
            }
        }
        
        return result[m - 1][n - 1];
    }
}

実際には、このアイデアはすでにある动态规划カテゴリ、我々はWikipediaの定義を見て

動的計画法は、(英語:動的プログラミングは、DPと呼ばれる)数学の一種で、経営学、コンピュータサイエンス、比較的簡単な方法のサブ問題に元の問題によって複雑な問題を解決するのに使用するための経済学とバイオインフォマティクスです方法。

最初は私のような感じ分治法の両方がサブ問題に大きな問題を打破する必要がありますが、あるため、分治法最終的にはサブ問題をマージしますが、动态规划そうではありません。

最適化

私たちは、最適化することができない場所がありません、このようなアプローチについて考えます。

最初はスペースの最適化は、我々は2次元配列を使用しなければならないのですか?一次元配列を代わりに使用することができますか?

各点の計算とは、隣接する点の上部のみを残し、従って、より遠い点を必要としないので、答えはイエスです。

一次元配列

唯一の一次元配列であれば、行の結果を格納するだけ、計算された時間が連続して交換された場合、次の行、コード:

class Solution {
    public int uniquePaths(int m, int n) {
        int[] dp = new int[m];
        int j;
        for(int i = 0; i < n; i++) {
            for(j = 0; j < m; j++) {
                if(j == 0) {
                    dp[j] = 1;
                }
                else {
                    // 其他的点都可以由左边的点和上面的点到达
                    dp[j] += dp[j-1];
                }
            }
        }

        return dp[m-1];
    }
}

このような最適化はほとんど終わりました。私たちはアイデアからそれを最適化することができれば?

組み合わせの数

我々は唯一の右または下の2つの選択肢があり、そして我々は、総パスを持って、実際に行くので(m-n-2)その、(m-1)右されているパス(n-1)のパスがダウンしている、実際には、に変換することができます。

(m-n-2)ピック(m-1)の組み合わせ、すなわち数、C((m-n-2), (m-1))

だから我々は、コードを書くことができます。

class Solution {
    
    public int uniquePaths(int m, int n) {
        // 用double,因为计算出的数值会很大
        double num = 1, denom = 1;
        // 找出更小的数,这样可以减少计算次数和计算出的数值
        int small = m > n ? n : m;
        
        for (int i = 1; i <= small - 1; ++i) {
            num *= m + n - 1 - i;
            denom *= i;
         }
        
         return (int)(num / denom);
    }
}

概要

トピック自体は難しくありませんが、ポイントはまだ話し合うことがたくさんある、と質問があれば、以下のコメントを残してくださいが、この質問は、私はいくつかの思考と思考をしなければならないすべてです。
私の公共の番号を追跡することができます興味を持っている、多分驚きがあるでしょう。

ここに画像を挿入説明

おすすめ

転載: www.cnblogs.com/death00/p/11505805.html