該当するシーン:
サブ問題は元の問題と同じ性質のものです
サブ問題は相互に関連しています(分割統治アルゴリズムとは異なります)
最適なソリューションを求めて、コアは網羅的です
動的計画機能:
- 重複するサブ問題
- 状態遷移方程式(最も重要)
- 最適な下部構造
動的計画問題解決フレームワーク
- 規範事例
- 網羅的な状態
- 状態遷移
古典的なケース1:フィボナッチ数列
f [1] = 1
f [2] = 2
f [n] = f [n-1] + f [n-2] //状態遷移方程式
フィボナッチ数列は、本物の動的計画問題ではなく、少なくとも最適解を探す問題ではありません。
コードの実装-再帰(暴力的な再帰)
/*
斐波那契数列
暴力递归
*/
#include <stdio.h>
#include <stdlib.h>
int fib(int n) {
// base_case
if (n ==1 || n ==2) {
return n;
}
// 递归调用
return fib(n - 1) + fib(n - 2);
}
int main() {
int n = 200;
printf("fib(%d) = %d\n", n, fib(n));
return 0;
}
再帰法は非効率的です。繰り返し計算が多く、時間計算量はO(2 ^ n)であり、時間がかかります。
コードの実装-再帰的メソッド(トップダウン/メモ付き)
最初に上から下に再帰し、次に下から上に戻ります。
/*
斐波那契数列
带备忘录的递归
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int helper(int *memo, int n) {
// base_case
if (n ==1 || n ==2) {
return n;
}
// 备忘录查询
if (memo[n] != 0) return memo[n];
memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
return memo[n];
}
int fib(int n) {
// 备忘录初始化
int *memo = new int[n + 1];
memset(memo, 0, n+1);
// 进行带备忘录的递归
return helper(memo, n);
}
int main() {
int n = 50;
printf("fib(%d) = %d\n", n, fib(n));
return 0;
}
現在、すべてのノードは1回だけ計算されるため、時間計算量はO(n)です。
追加の新しいメモリ割り当てのため。したがって、スペースの費用がもう1つあるため、スペースの複雑さはO(n)になります。
メモ付きの再帰的解決策は時間の空間です
コードの実装-反復法(ボトムアップ)
/*
斐波那契数列
带备忘录的递归
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int helper(int *memo, int n) {
// base_case
if (n ==1 || n ==2) {
return n;
}
// 备忘录查询
if (memo[n] != 0) return memo[n];
memo[n] = helper(memo, n - 1) + helper(memo, n - 2);
return memo[n];
}
int fib(int n) {
// 备忘录初始化
int *memo = new int[n + 1];
memset(memo, 0, n+1);
// 进行带备忘录的递归
return helper(memo, n);
}
int main() {
int n = 50;
printf("fib(%d) = %d\n", n, fib(n));
return 0;
}