硬币问题
现有若干枚硬币,硬币面值分别为1,5,11.要凑出价值为w,至少需要多少枚硬币。比如说w=15.
如果按照贪心的思维:
我们每次选取面值最大的硬币去尝试,
15
=
1
∗
11
+
4
∗
1
15=1*11+4*1
15=1∗11+4∗1
所以,凑出价值w=15,至少需要的硬币数为1+4=5枚。
但是,实际上我们只需要取3枚面值为5的硬币,就可以搞定。所以,此题,贪心思维,不靠谱!
分析
记凑出价值n所需要的最少硬币数为
f
(
n
)
f(n)
f(n)
那么可以得到下面这个表:
可见
f
(
n
)
f(n)
f(n)只与
f
(
n
−
1
)
,
f
(
n
−
5
)
,
f
(
n
−
11
)
f(n-1),f(n-5),f(n-11)
f(n−1),f(n−5),f(n−11)有关。
f
(
n
)
=
min
{
1
+
f
(
n
−
1
)
,
1
+
f
(
n
−
5
)
,
1
+
f
(
n
−
11
)
}
f(n)=\min\{1+f(n-1),1+f(n-5),1+f(n-11)\}
f(n)=min{1+f(n−1),1+f(n−5),1+f(n−11)}
总结
- 最优子结构
大问题的最优解可以由小问题的最优解推出 - 无后效性
一旦 f ( n ) f(n) f(n)确定,那么之后直接调用它的值就可以,不需要再去关心 f ( n ) f(n) f(n)的计算过程。
代码
#include<iostream>
#include<string.h>
using namespace std;
const int INF = 0x3f3f3f3f;
#define W 15
#define m 3
int dp[W + 1]; // 存放最少硬币个数信息
int S[W + 1]; // 存放硬币的面值信息
int main() {
int c[m] = {1, 5, 11};
memset(dp, INF, sizeof(dp));
dp[0] = 0;
for (int w = 1; w <= W;++w) {
for (int i = 0; i < m;++i) {
if(c[i]<=w && dp[w]>(1+dp[w-c[i]])){
dp[w] = 1 + dp[w - c[i]];
S[w] = i;
}
}
}
for (int i = 0; i < W;++i) {
cout << dp[i] << " ";
}
cout << endl;
for (int i = 0; i < W;++i) {
cout << S[i] << " ";
}
cout << endl;
cout << dp[W] << endl; // 最少硬币数
int n = W;
while(n>0) {
cout << c[S[n]] << " "; // 输出是哪些硬币
n = n - c[S[n]];
}
system("pause");
return 0;
}