一、問題描述
有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 #include5using
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...