動的計画-はじめに

動的プログラミング

ウィキペディアでは、動的プログラミングを次のように定義しています。

動的プログラミングは、複雑な問題をより簡単なサブ問題のコレクションに分解し、それらの各サブ問題を一度だけ解決し、それらのソリューションを保存することによって解決する方法です。
参照:動的プログラミング

つまり、動的プログラミングには次の3つの特性が必要です。

  1. 元の問題をいくつかの同様のサブ問題に分解します。(「類似のサブ問題」の強調)
  2. すべての副問題は一度だけ解決する必要があります。(「一度だけ解決する」の強調)
  3. 解を副問題に保存します。(「保存」の強調)

それでは、動的プログラミングで問題を解決できるかどうかをどのように判断するのでしょうか?

問題を解くための最適性の原理が成り立つ場合、それは動的計画法によって解くことができます

プロセスの初期状態と初期決定に関係なく、残りの決定は、初期決定によって生成された状態に対して最適な決定シーケンスを形成する必要があります。いくつかの例を通じて動的プログラミングを学びましょう。

1.数の塔の問題

問題の説明

        7 
      3   8 
    8   1   0 
  2   7   4   4 
4   5   2   6   5

いくつかの数字が書かれたR列の計数タワーがあります。Q.タワーの最高点から最下部まで、すべてのパスで渡された数値の最大合計はいくつですか?
上の図に示すように、これは5列の計数塔で、7-3-8-7-5のパスが数を通過し、最大は30です。

ソリューションのアイデア

数の塔の問題に直面すると、貪欲アルゴリズムを使用することは明らかに実行不可能です。たとえば、貪欲アルゴリズムを使用する場合、選択されるパスは7-8-1-7-1-5であり、数の後の数は28のみであり、最大ではありません。ディープサーチDFSを使用すると、時間の複雑さをO(2N-1)として簡単に計算できます(各数値には左下と右下の2つのオプションがあるため)、行数が多いほど、タイムアウトする必要があります。

したがって、データタワーの問題には動的プログラミングアルゴリズムが必要です。

①上から下へトラバースできます。

番号を渡したい場合は、左上隅または右上隅の番号からしか到達できないことがわかります。

したがって、明らかに、任意の数値Aを渡す場合、パスが通過する最大の数値は、数値Aの左上にある数値Bであり、右上にある数値Cです。あれ、それにその数A

したがって、状態遷移方程式は次のとおりです。 [公式]

ここで、i、jは行と列の数を表し、dpはストレージの最大合計を表し、numはその位置の数を表します。

[公式]左上隅と[公式]右上隅を示します

例で説明すると、3行目の数値1を渡す場合、最初に左上隅の数値3と右上隅の数値8の最大合計を調べます。3には明らかに7-3パスしかないため、最大合計は10です。8にも明らかに7-8パスしかないため、最大合計は15です。2つのうち大きい方は15なので、1の後に到達できる最大合計は15 + 1 = 16。

これは段階的に行き来し、最終的に、最下行から最大値が検出されて出力される限り、各位置を通過した後に到達できる最大合計が取得されます。

②下から上へ移動することもできます。

パスが上から下へ行く場合でも、下から上へ行く場合でも、その数と通過する数は同じであるため、この質問は需要に変換できます。これは、下から最大のポイントに移動する最大数の合計です。

書き込み方法は、状態遷移時に最大値が数値の左下隅と右下隅から取得されることを除いて、シーケンシャルトラバーサルと同じです。順次走査と比較して、逆走査で書き込むことの利点は、最後のステップで最後の行の最大値を見つけるプロセスが少なく、最高点に格納された値を直接出力できることです。

コードの実装

#include <stdio.h>
#include <algorithm>
using namespace std;//这里以顺序遍历为例
int num[1005][1005];//用于储存数塔每个位置的数字
int dp[1005][1005];//用于储存经过数塔每个位置所能达到的最大和
int main()
{
    int r;
    scanf("%d",&r);//输入数塔行数
    for(int i=1;i<=r;i++)
        for(int j=1;j<=i;j++)
            scanf("%d",&num[i][j]);
    //输入数塔数据,注意i和j要从1开始,防止数组越界
    for(int i=1;i<=r;i++)//共计r行
        for(int j=1;j<=i;j++)//每行有j个数字
            dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+num[i][j];
    //经过该数字的最大和,为左上角和右上角中的max,再加上该数字
    int ans=0;
    for(int i=1;i<=r;i++)
        ans=max(ans,dp[r][i]);//从最后一行中找到最大数
    printf("%d\n",ans);//就是答案
    return 0;
}

おすすめ

転載: www.cnblogs.com/fluxation/p/12693486.html