硬幣問題 記憶化搜尋與遞推的轉換

2022-05-02 11:42:09 字數 1798 閱讀 7706

一、問題描述

有n種硬幣,面值分別為v1,v2,v3,...,vn,每種都有無限多。給定非負整數s,可以選用多少硬幣,使得面值之和恰好為s?輸出硬幣數目的最小值和最大值。1≤n≤100,0≤s≤10000,1≤vi≤s.

二、解題思路

將每種面值看作乙個點,表示「還需湊足的面值」,則初始狀態為s,目標狀態為0。對於狀態i,如果存在硬幣j,i ≥ vj,則說可從狀態 i 轉到

i - vj,即存在 i--> i-vj 的有向邊。之前矩形巢狀沒有固定起點、終點,直接求圖上的最長路。這題由於硬幣數量無限,圖是無限大的,所以必須固定起點和終點(其實就是將圖的大小固定)。然後求起點s到終點0的最長路和最短路。

三、**實現

**一:對最長路、最短路分別使用dp

1 #include2 #include3 #include4 #include5

using

namespace

std;67

const

int inf = 0x3f3f3f3f;8

const

int maxn = 100 + 10;9

const

int maxs = 10000 + 10;10

intn,v[maxn];

11int dmin[maxs],dmax[maxs]; //

d[i]表示從節點i出發到節點0的最長路徑的長度

12int

s;13

14int dp(int

s)15

24return

ans;25}

2627

int dp2(int

s)28

37return

ans;38}

3940

void

slove()

4154

55int

main()

5664

return0;

65 }

**二:使用兩次dp有點繁瑣,使用遞推式,就能夠合二為一了。

1

//dmin[i]表示從節點i出發到節點0的最短路徑的長度2//

dmax[i]表示從節點i出發到節點0的最長路徑的長度

3int

dmin[maxs], dmax[maxs];

4void

slove()

511 dmin[0] = dmax[0] = 0;12

13for(int i = 0;i <= s;i++) //

注意迴圈的順序,由小到大

14for (int j = 0; j < n; j++)

1521}22

23int res1 = dmax[s], res2 =dmin[s];

24if (res1 < 0) printf("

impossible\n

"); //

此時res1是乙個略大於 -inf 的數

25else printf("

%d\n

", res1);

26if (res2 == inf) printf("

impossible\n");

27else printf("

%d\n

", res2);

28 }

這也再一次告訴我們,遞迴和迴圈,記憶化搜尋和遞推可以轉化。

記憶化搜尋和遞推對每個狀態值都只計算一次,前者需要遞迴實現,後者當前狀態只與之前狀態有關,由於是從最初始的出發,計算當前狀態時,保證了之前的狀態值已經被求出。

樹形結構計數問題 記憶化搜尋

分析 這裡要求的是全部節點的距離和,可以定義子問題為 某乙個節點去掉它的某一條邊而形成的子樹中,該節點的距離和以及該子樹的節點數.只需要知道全部節點的該資訊,對每個節點就可以通過其臨界節點的該資訊得到這個節點的距離和.總的時間代價是o 2 n 1 o 2 n 1 o 2 n 1 的.相比於o n 2...

DP系列 遞推 遞迴 記憶化搜尋

一開始我想,從上往下的話,可以用遞迴,搜尋 dfs 所有可能的路徑,如下 int dfs int i,int j 從第一層開始遞迴到最後一層再從最後一層往回遞迴,一共遞迴2的n次方,顯然會t,那麼要怎麼優化一下呢?我們看到,在題中第三排的1會被第二排的3和8遞迴兩次,經歷了一次重複計算,這時我們可以...

wenbao與記憶化搜尋

記憶化搜尋 通俗地講就是搜尋的形式,dp的思想 一些搜尋難以完成,dp的動態轉移方程又不好寫的題,就會用到記憶化搜尋,利用dp記錄路徑 相當於為dfs剪枝 用dfs進行模擬。啦啦啦啦啦啦,好厲害!弱雞 1 include 2 include 3 include 4 using namespace s...