秋招准备100天---02

换钱的最小货币数

给定一个target,代表钱,再给一个数组money,代表货币种类,比如有【1,2,5,10】,把target换成零钱,最少的货币数。如果不能把钱找开,则返回-1,taget = 0 ,返回0;


方法1: 第一感觉是用贪心算法做。

int minMoney(vector& money,int target) {
    if (target < 0)
        return -1;
    sort(money.begin(),money.end());
    int res = 0;
    int i = money.size();
    while(target && i > 0) {
        int temp = target / money[i-1];
        res += temp;
        target = target % money[i-1];
        i--;
    }
    if (target > 0)
        return -1;
    return res;
}

如果target=29,money = {2,3,4},应该是6张4元的,一张2元,一张3元,但是算法给出7张4元,所以返回了-1.所以这样是错误的,不能用贪心。


方法2:用动态规划,dp[i][j] 表示用前i个货币,组成j元的货币数,那么转移方程就是

dp[i][j] = min(dp[i-1][j],dp[i][j-money[i]]+1)

j元组成数,要么不需要第i种货币,如果需要第i个货币,则是dp[i][j-money[i]] + 1;

时间复杂度和空间复杂度都是O(money.size() * target)

int minMoney2(vector& money,int target) {
    sort(money.begin(),money.end());
    int n = money.size();
    if (target < 0)
        return -1;
    vector> dp(n,vector(target+1,INT_MAX));
    for (int i = 0; i < n; i++)
        dp[i][0] = 0;
    for (int j = 1; j <= target; j++)
        if (j >= money[0] && dp[0][j-money[0]] != INT_MAX)
            dp[0][j] = dp[0][j-money[0]] + 1;
    for (int i = 1; i < n; i++) {
        for (int j = 1; j <= target; j++) {
            if (j >= money[i]) {
                if (dp[i][j - money[i]] == INT_MAX)
                    dp[i][j] = dp[i - 1][j];
                else
                    dp[i][j] = min(dp[i - 1][j], dp[i][j - money[i]] + 1);
            }
            else {
                dp[i][j] = dp[i-1][j];
            }
        }
    }
    if (dp[n-1][target] == INT_MAX)
        return -1;
    return dp[n-1][target];
}

方法3:动态规划,但是压缩了空间。

int minMoney3(vector &money,int target) {
    int n = money.size();
    sort(money.begin(),money.end());
    vector dp(target+1,INT_MAX);
    dp[0] = 0;
    for (int j = 1; j <= target; j++) {
        if (j >= money[0] && dp[j-money[0]] != INT_MAX)
            dp[j] = dp[j - money[0]] + 1;
    }
    for (int i = 1; i < n; i++) {
        for (int j = 1; j <= target; j++) {
            if (j >= money[i] && dp[j-money[i]] != INT_MAX)
                dp[j] = min(dp[j-money[i]]+1,dp[j]);
        }
    }
    if(dp[target] == INT_MAX)
        return -1;
    return dp[target];

}

换钱的最小货币数(补充问题)

和上一个题目不同的是,每一个货币不是无限次使用,比如说,money = {2,3,5,5},那么2,3只能用1次,5用2次。
dp[i][j]表示j元,只用前i张零钱,最小货币数,换不开则是INT_MAX,所以有

dp[i][j] = min(dp[i-1][j-money[i]]+1,dp[i-1][j])

方法1:二维dp,时间空间复杂度都是O(M*N),M表示money数组数,N表示target大小。

int minCoins3(vector &money,int target) {
    int n = money.size();
    if (target < 0)
        return -1;
    sort(money.begin(),money.end());
    vector> dp(n,vector(target+1,INT_MAX));
    for (int i = 0; i < n; i++) dp[i][0] = 0;
    for (int j = 1; j <= target; j++) {
        if (j == money[0])
            dp[0][j] = 1;
    }
    for (int i = 1; i < n; i++) {
        for (int j = 1; j <= target; j++) {
            if (j >= money[i] && dp[i-1][j-money[i]] != INT_MAX) {
                dp[i][j] = min(dp[i-1][j-money[i]]+1,dp[i-1][j]);
            }
            else {
                dp[i][j] = dp[i-1][j];
            }
        }
    }
    if (dp[n-1][target] == INT_MAX)
        return -1;
    return dp[n-1][target];

}

方法2:压缩DP。绝妙的部分在于内部循环是从后向前循环的

int minCoins4(vector &money,int target) {
    int n = money.size();
    if (target < 0)
        return -1;
    sort(money.begin(),money.end());
    vector dp(target+1,INT_MAX);
    dp[0] = 0;
    if (money[0] <= target)
        dp[money[0]] = 1;
    for (int i = 1; i < n; i++) {
        for (int j = target; j > 0; j--) {//精彩
            if (j >= money[i] && dp[j-money[i]] != INT_MAX)
                dp[j] = min(dp[j],dp[j-money[i]]+1);
        }
    }
    if (dp[target] == INT_MAX)
        return -1;
    return dp[target];
}

换钱的方法数

这个相当于把搜索路径都要输出。


方法1:用DFS。

void dfs(vector &money,int target,int &res,int sum,int pos) {
    if (sum >= target) {
        if (sum == target) res++;
        return;
    }

    for (int i = pos; i < money.size(); i++) {
        dfs(money,target,res,sum+money[i],i);
    }

}

你可能感兴趣的:(100天,秋招准备100天)