背了模板就会背包题吗?(某厂真题)|刷题打卡

1,701 阅读2分钟

掘金团队号上线,助你 Offer 临门! 点击 查看详情

一、题目描述:

image.png

二、思路分析:

口袋n张钱,买价值m的玩具,明摆着背包了。走之前讲过的动态规划流程1动归流程细分

确定状态

状态表示

dp[i][j] : 第i张钱, 价值j

状态计算

dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j - 5] + dp[i - 1][j - 10] + dp[i - 1][j - 50];

真的是这样吗?

以上的代码会造成 1 1 5 和 1 5 1重复出现的可能,会导致数据变得错误,因此换一种思路

思路二

状态表示

dp[i][j]: 第i种钱, 价值j arr = {1, 5, 10, 50} dp[0][j]: 仅使用1元纸币,价值j

这样的话就是{1,1,1,1,1} j个1组成的数组

dp[1][j]: 仅使用1元纸币,5元纸币。价值为j。

dp[1][j] = dp[0][j - 5] + dp[0][j - 10] + ...

这样的话就能避免思路1出现的结果重复。

最后循环一次dp dp[i][j].size() 如果小于 n 就ret++

三、AC 代码:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int buytoys(int n, int m){
    // 共有n个票,1,5,10,50
    int coins[] = {1, 5, 10, 50};
    vector< vector< vector<int> > > arr;
    for(int i = 0; i < m + 1; i++){
        vector<vector<int> > tmp;
        if(i == 0){
            vector<int> asp;
            tmp.push_back(asp);
        }
        arr.push_back(tmp);
    }

    //arr[0]
    for(int i = 0; i < 4; i++){
        for(int j = coins[i]; j < m + 1; j++){
            if(arr[ j - coins[i]].size() != 0){
                vector <vector<int> >  tmp = arr[ j - coins[i]];
                for(int k = 0; k < tmp.size(); k++){
                    tmp[k].push_back(coins[i]);
                }
                arr[j].insert(arr[j].end(),tmp.begin(), tmp.end());
            }
        }
    }
    int ret = 0;
    int b = arr.size() - 1;

    for(int i = 0; i < arr[b].size(); i++){
        if(arr[b][i].size() <= n){
            ret++;
        }
    }
    return ret;
}

int main(){
    //这个输出为4
    // [50] 使用了1张
    // [10] 使用了5张
    // [10] 使用了4张 [5]使用了2张
    // [10] 使用了3张 [5] 使用了4张

    // 如果要求必须使用n张,而不是n张以下欸,则只有“[10] 使用了3张 [5] 使用了4张”满足条件

    cout << buytoys(7, 50) <<endl;
}

四、总结:

相对于普通的背包,这道题以第几类钱为第一维还是有挑战性的,比较难想。不过上述代码并不是完全体,还是可以剪枝的。掘友可以自行更新。