力扣第 115 场双周赛(完全背包,多重背包,前缀和,最长上升子序列模型)

 模拟题,记录一个k值,表示上一次记录到哪里了。若遇到prev则移动k;否则重置k;

class Solution {
public:
    vector<int> lastVisitedIntegers(vector<string>& words) {
        vector<int> nums, res;
        int k = 0;
        for(auto &w: words) {
            if(w == "prev") {
                k ++ ;
                if(k > nums.size()) res.push_back(-1);
                else res.push_back(nums[nums.size() - k]);
            } else {
                nums.push_back(stoi(w));
                k = 0;
            }
        }
        
        return res;
    }
};

 本题可以用贪心,也可以用dp。

贪心方法:我们可以把groups进行集合的划分,分为一个个由若干0和若干1组成的串。在划分后的集合随便取出一个数即可(这里我们取第一个)。

class Solution {
public:
    vector<string> getWordsInLongestSubsequence(int n, vector<string>& words, vector<int>& groups) {
        vector<string> res;
        for(int i = 0, last = -1; i < n; i ++ ) {
            if(groups[i] != last) {
                res.push_back(words[i]);
                last = groups[i];
            }
        }

        return res;
    }
};

 

 

 第三题是一种记录方案的最长子序列问题,其数据范围为1000,可以用最长上升子序列的模板。

class Solution {
public:

    bool check(string& a, string &b) {
        if(a.size() != b.size()) return false;
        int cnt = 0;
        for(int i = 0; i < a.size(); i ++ ) {
            if(a[i] != b[i]) cnt ++ ;
        }
        return cnt == 1;
    }

    vector<string> getWordsInLongestSubsequence(int n, vector<string>& words, vector<int>& groups) {
        vector<int> f(n, 0), g(n, -1);
        int k = 0;
        for(int i = 0; i < n; i ++ ) {
            f[i] = 1;
            for(int j = 0; j < i; j ++ ) {
                if(groups[i] != groups[j] && check(words[i], words[j])) {
                    if(f[i] < f[j] + 1) {
                        f[i] = f[j] + 1;
                        g[i] = j;
                    }
                }
            }
            if(f[i] > f[k]) k = i;
        }

        vector<string> res;
        while(k != -1) {
            res.push_back(words[k]);
            k = g[k];
        }

        reverse(res.begin(), res.end());
        return res;
    }
};

 

 

 

 完全背包,多重背包,前缀和的应用。

每个数字抽象为一个物品,体积为数值。

状态定义 f[i][j]:从前i项中选,体积恰好为j的方案数。

f[i][j] = f[i-1][j] + f[i-1][j-i] + f[i-1][j- i * 2] + ..... + f[i-1][j - cnt * i (如果它大于0)]

可以利用完全背包 + 前缀和来进行计算。

完全背包g[i][j] 可以取无限个i 。其对应关系为 f[i][j] = g[i][j] - g[i][j - (cnt[i] + 1) * i         <-          g(r) - g(l - 1)

class Solution {
public:
    const int N = 20010, MOD = 1e9 + 7;
    int countSubMultisets(vector<int>& nums, int l, int r) {
        vector<int> cnt(N, 0), f(r + 1), g(r + 1);
        for(auto x: nums) cnt[x] ++ ;
        f[0] = 1;

        for(int i = 1; i < N; i ++ ) {
            if(!cnt[i]) continue;
            for(int j = 0; j <= r; j ++ ) g[j] = 0;
            for(int j = 0; j <= r; j ++ ) {
                g[j] = f[j];
                if(j >= i) g[j] = (g[j] + g[j - i]) % MOD;
            }

            for(int j = 0; j <= r; j ++ ) {
                f[j] = g[j];
                if(j >= (cnt[i] + 1) * i)
                    f[j] = (f[j] - g[j - (cnt[i] + 1) * i]) % MOD;
            }
        }


        int res = 0;
        for(int i = l; i <= r; i ++ ) res = (res + f[i]) % MOD;
        res = res * (cnt[0] + 1ll) % MOD;
        res = (res + MOD) % MOD;

        return res;
    }
};

 

posted @ 2023-10-15 03:23  深渊之巅  阅读(14)  评论(0编辑  收藏  举报