タイトル説明
したがって、最大数の経路を介して和こと、パスの端部側の上から、図中の三角形を見つけます。パス上のすべてのステップは、下の左または右に離れて下げることができますのみ。これは最大値を必要とし、特定のパスを与えないことができます。
図1は、三角形の線の数よりも大きいが100未満に等しい0--99にデジタルであります
入力フォーマット:
5 //表示三角形的行数 接下来输入三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
要件と最大出力
分析
開始頂点から、最もを求めています。各ポイントは2つの選択肢があり、左下または右下、大は知らない左と右下を下げ、我々は左下得られ、それぞれ、新たな三角形と見られて次の2点、三角形を追加し、直角三角形と最大パスを下げることができます、簡単に適用することができる現在の頂点の大きい選択。これは再帰的なプロセスです。
f(int[] arr,int i,int j){
return arr[i][j]+max(f(arr,i+1,j),f(arr,i+1,j+1));
}
擬似コードは、この時間は、i、jは増加する方法がありませんので、最後の行では、再帰的、輸出の輸出を考慮すると、その頂点が散歩を下るし続けないと等価である、プラス記号の後に省略部分されていませんそれはすることができます。
再帰的なコード
/**
*
* @param triangle
* 数字三角形
* @param i
* 起点行号
* @param j
* 起点列号
* @return 计算出的最大和
*/
public static int maxSumUsingRecursive(int[][] triangle, int i, int j) {
int rowIndex = triangle.length;
if (i == rowIndex - 1) {
return triangle[i][j];
} else {
return triangle[i][j]
+ Math.max(maxSumUsingRecursive(triangle, i + 1, j),
maxSumUsingRecursive(triangle, i + 1, j + 1));
}
}
私はPOJに提出したときに、この再帰的なコードについては、それは次のような結果が表示されます:
時間が不足しているコードのうち、なぜ残業のですか?我々はダブルカウントされているので、答えは、簡単です。左下の頂点のそれぞれ右に2の仮想三角形を下げ、二つの三角形がオーバーラップしています。私たちは、コンピュータのヘルプを再帰的に行ったとき、私たちは次のように計算をグラフ:
最適化1:記念再帰
我々は計算MaxSum番号8の第2行から起動したときに我々の計算MaxSumは、第2の行番号から開始から23の計算MaxSum 1の起動時に時間を計算し、番号1の第3行を取りますダブルカウントがあることを最初から一度MaxSum 1、。これは、多くの時間を無駄にします。つまり再帰法であれば、各パストラバーサルの深さ、繰り返し計算の多数の存在。時間計算量は確かに、N = 100行の2のn乗、アウトです。
次に、我々は、改善されたアイデアを改善する方法を検討する予定である:変化と、この例で検討再帰再帰プロセスパラメータを表すことができ、行と列数の2人の代表です。私たちは、次の直接使用するために使用、キャッシュされた位置(i、j)で計算した結果を置くことができます。
キャッシュアレイは、2次元である2つのパラメータがあります。
この考え方によると、我々はメモリ再帰動的なプログラミングプログラムを作り、上記のコードの改善を置くことができます。
/**
* 记忆型递归
* @param triangle
* @param i
* @param j
* @return
*/
public static int maxSumUsingMemory(int[][] triangle, int i, int j, int[][] map) {
int rowIndex = triangle.length;
int value = triangle[i][j];
if (i == rowIndex - 1) {
} else {
//缓存有值,便不递归
int v1 = map[i + 1][j];
if (v1 == 0) {
v1 = maxSumUsingMemory(triangle, i + 1, j,map);
}
//缓存有值,便不递归
int v2 = map[i + 1][j+1];
if (v2 == 0) {
v2 = maxSumUsingMemory(triangle, i + 1, j+1,map);
}
value = value
+ Math.max(v1, v2);
}
//放入缓存
map[i][j]=value;
return value;
}
時間計算量はN²、スペースの複雑さであるN²です。
再発
次に、ストレージマップキャッシュ値で見た、特定の値は、位置によって決定される値です。派生この例ではI、Jは、最大位置I + 1、jおよび位置I + 1、J + 1点の位置を算出するために必要。
その後、正確に定義された動的プログラミングは、従属位置を得るために、最初に計算されます。配列の最後の行を視野に私まで、我々は最後の行から計算した後、最後から二番目の行を計算し、そのために始めることができます。私たちはこのプロセスを完了するために、ステップバイステップでみましょう。
計算するには私たち最初の必要性は、以下のようにあなたは、最後の行に直接書き込むことができるように、最後のものです:
数の逆数の毎秒行の分析を開始、あなたは今、最後の行、最後の行を追加していると5を要約することができます番号2,2及び4を分析することができますさて、それは少し大きく、5が追加されていることは明らかである、結果は7、我々は7,7まで保存することができ、この時間は、7は、その後の数字を分析し、最後の5行を追加し、第二添加の最後の線であってもよいことができ、それはより5が追加されていることは明らかである、結果は、12でありますしたがって、我々は彼らに12を保存します。などなど。。私たちは、以下の画像を取得することができます:
そして、第三と一番下の行から4番目の同じトークン解析逆数で、最終的な分析の最初の行は、我々は次のような結果を変えることができます:
明らかに、我々は2次元の動的プログラミングテーブルを使用することができますが、対象の正確なパスを必要としない、我々は特定のコードを参照して、繰り返し使用することができる一次元配列を使用して、スペースを圧縮することができます。
public static int maxSumUsingDp(int[][] triangle, int i, int j) {
int rowCount = triangle.length;
int columnCount = triangle[rowCount-1].length;
int[] states = new int[columnCount];
for (int k = 0; k < columnCount; k++) {
states[k] = triangle[rowCount-1][k];
}
for (int row = rowCount-2; row >= 0; row--) {
for (int col = 0; col < triangle[row].length; col++) {
states[col] = triangle[row][col]+Math.max(states[col], states[col+1]);
}
}
return states[0];
}
概要
次に、我々は総括していました。
ゲージ一般的な再帰的な変換方式の移動:
Nは、再帰関数のパラメータの変更を有する配列を定義するために、配列のサイズはn次元パラメータ値の範囲、と呼ばれるパラメータの組み合わせの配列指標値の代表であり、要素の値は、この状態の値です。
状態が繰り返し計算されている場合は、状態の値は、このように計算の数を減らし、補助配列をチェックする必要がある場合、計算された値を格納するための二次配列を確立することができます。
逆再帰的なプロセスは、補助アレイのキャッシュ値の形成を考えます。再帰は、可動一次計算ルールが計算された値に依存するように定義される、依存層です。徐々に配列を埋め、境界値を開始。
ナップザック問題は、*バックパックの重量二次元アレイ、項目の数であります
問題は、鋼棒2次元配列、長さ切断モードであります*
問題は、本実施形態の二次元アレイであり、iがjで*
再帰メモリタイプ - - これらの問題は、再帰的に続くことができる道路の動的プログラミング最適化を。