硬币问题

现有若干枚硬币,硬币面值分别为1,5,11.要凑出价值为w,至少需要多少枚硬币。比如说w=15.
如果按照贪心的思维:
我们每次选取面值最大的硬币去尝试,
15=1∗11+4∗115=1*11+4*115=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)}

总结

  1. 最优子结构
    大问题的最优解可以由小问题的最优解推出
  2. 无后效性
    一旦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;
}

更多推荐