そこで質問、この質問は、動的プログラミングへの暴力再帰の最適化ルーチンを感じるために使用されます。暴力の方法を考えてみてください、そして、ダイナミックプログラミングに最適化されています。最初は、元の問題をしようとする暴力の方法です。唯一の方法は、最も困難な最も重要であると思い付くしてみてください。
私たちは、最初のタイトルを見て:
あなたの配列arrを与えるために、繰り返されていない値は正の数、および整数の目的であるすべてのARR。あなたはARRの任意の数を選択することができた場合は、各番号は、一度に選択することができ累積的にAIMを合算することができ、trueまたはfalseを返します。
同様のアイデアサブ問題列:レッツF(I、和)iは、配列インデックスであり、合計は現在の要素に配列要素の開始を表し、ARRする= {3,2,7,13}、目的= 9、例えば、再帰的プロセスを以下に示す:(PS:これは私は図を理解することができている、O(╯□╰)O)
だから、次の手順を解決するために:
1. 書き込みの試み(再帰)バージョン
public static boolean money1(int[] arr, int aim) {
return process1(arr, 0, 0, aim);
}
public static boolean process1(int[] arr, int i, int sum, int aim) {
if (i == arr.length) {
return sum == aim;
}
//继续往下执行,两种情况:要么选要么不选
return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);
}
2. 満たすために、動的プログラミングを転送するための条件かどうかを分析します。
(1)がたくさんあり、二重カウントは:上記の例は、明らかに十分ではありません{3,2,5,13}を交換することができ、それは第三のステップ2つのF(3,5)に表示され、F(2 5)+0、F(2,0)によって得られた+5、およびF(3,5)二重カウントがあり、肯定的に同じ返り値に従います。
(2)問題が属する「NO後遺症」問題:1を用いて、F(3,5)は関係なく、パスが到達が戻り値を取得したのと同じであり、それが「ない後遺症」を意味しません
レビューは、動的計画法に変換することができます。
前記 可変パラメータの分析、状態に戻り、いくつかの可変パラメータ、及びそれらの変動範囲を表すいくつかの変数パラメータの値は、いくつかのテーブル次元DPを構成することができます。
アレイを目的と不変、I、和変数。
4. 基本ケースを参照してください、記載されている場所に依存しません。(配列{ 3、2、。5 }、AIMは= 7は、例えば、作業負荷を減らします)
2次元テーブルの行の代表iは、列はすべての要素の合計を表し、それは(この数を超えないように、原理的に目指して、それは場合により戻り偽より明らかです)。
和== Tである場合の目的は、他方がFである場合にのみ、基本ケースの最後の行として知られています コンテンツの使用可能な次の表は、そう:
I \ SUM | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
0 | |||||||||||
1 | |||||||||||
2 | |||||||||||
3 | F | F | F | F | F | F | F | T | F | F | F |
分析は、一般的な位置に依存しています。
return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);
共通の位置の値を知ることが、見出され、我々は次の行の値と次の列と右側の列にその付加価値ARR [I]を知っている必要があります。例えば、F(2,0)、上記Fに依存するコード、(3,0)およびf(3,5)から見て、その結果、F(2,0)Fに対して(F(3,0)およびf(3なぜなら、5))は両方ともFです。
レビューは、順番に全体のフォームに必要事項を記入することができるようになります最後の行を、知っています。
I \ SUM | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
0 | T | F | T | F | T | T | F | T | F | F | F |
1 | T | F | T | F | F | T | F | T | F | F | F |
2 | F | F | T | F | F | F | F | T | F | F | F |
3 | F | F | F | F | F | F | F | T | F | F | F |
次のように全体のコードは次のとおりです。
package com.gxu.dawnlab_algorithm8;
/**
* 换钱问题
*
* @author junbin
*
* 2019年7月12日
*/
public class Money_Problem {
public static boolean money1(int[] arr, int aim) {
return process1(arr, 0, 0, aim);
}
public static boolean process1(int[] arr, int i, int sum, int aim) {
if (sum == aim) { //如果遍历过程中满足该条件,则停止后续遍历
return true;
}
// sum != aim
if (i == arr.length) { //如果已经到了数组末尾,则直接输出
return false;
}
//继续往下执行,两种情况:要么选要么不选
return process1(arr, i + 1, sum, aim) || process1(arr, i + 1, sum + arr[i], aim);
}
//动态规划
public static boolean money2(int[] arr, int aim) {
boolean[][] dp = new boolean[arr.length + 1][aim + 1];//列改为aim+1可以更节省空间
for (int i = 0; i < dp.length; i++) {
dp[i][aim] = true;
}
for (int i = arr.length - 1; i >= 0; i--) {
for (int j = aim - 1; j >= 0; j--) {
dp[i][j] = dp[i + 1][j];
if (j + arr[i] <= aim) {
dp[i][j] = dp[i][j] || dp[i + 1][j + arr[i]];
}
}
}
return dp[0][0];
}
public static void main(String[] args) {
int[] arr = { 1, 4, 8 };
int aim = 12;
System.out.println(money1(arr, aim));
System.out.println(money2(arr, aim));
}
}
概要:今まで2つの暴力的なターンの古典的な再帰的な動的なプログラミングトピックを経験しているし、あなたが書いたとDPテーブルステージバージョンを描画しようとするルーチンの高度を感じるが、それでも非常に熟練した感じ、特に最も困難ですることができ、続けてください!