leetcode2902 和带限制的子多重集合的数目
给定数组nums[n]和整数l,r,nums中的元素可能会重复,要求从nums中选择若干个元素,其元素和在[l,r]内,有多少种不同方案,结果对1E9+7取模。注:空集合的结果为0,相等的元素之间没有区别。
1<=n<=2E4; 0<=nums[i]<=2E4; sum(nums[i])<=2E4; 0<=l<=r<=2E4
分析:
1、存在相等元素,且没有区别,可以看成是一个多重背包问题。
2、由于sum(nums[i])<=2E4,可以估算出不同元素的个数(记为m)不超过200,因为1+2+..+200>2E4。
3、如果用二进制优化,时间复杂度会超,因此要用类似单调队列优化dp的方式优化掉时间复杂度中的log,大致思路如下:
(1)状态转移方程:dp[i][j] = dp[i-1][j] + dp[i-1][j-v] + dp[i-1][j-2v] + dp[i-1][j-3v] + ...,可以看到第二维模v同余,即计算dp[i][j]时,只依赖dp[i-1]只与j同余的那些项。
(2)外层按余数0~v-1从小到大的顺序,内层按a, a+v, a+2v的顺序,计算dp。对于某个固定的余数,由于j较大时,未必有足够的相同元素,因此用滑动窗口来维护。
4、元素0需要单独处理。
int dq[20005]; class Solution { public: int countSubMultisets(vector<int>& nums, int l, int r) { int n = nums.size(); std::sort(nums.begin(), nums.end()); std::vector<std::pair<int,int>> vp; int cnt0 = 0; for (int i = 0, j = 0; i < n; i = j) { for (j = i; j < n && nums[j] == nums[i]; j++); if (nums[i] == 0) { cnt0 = j - i; } else { vp.emplace_back(nums[i], j - i); } } int m = vp.size(); std::vector<mint> dp1(r + 1), dp2(r + 1); for (int i = 0; i <= m; i++) { dp1[0] = 1; } for (int i = 1; i <= m; i++) { int val = vp[i-1].first; int cnt = vp[i-1].second; for (int j = 0; j < val; j++) { mint sum = 0; int L = 0, R = 0; for (int k = j; k <= r; k += val) { sum += dp1[k]; dq[R++] = k; if (dq[R-1] - dq[L] > val * cnt) { sum -= dp1[dq[L++]]; } dp2[k] = sum; } } std::swap(dp1, dp2); } mint ans = 0; for (int j = l; j <= r; j++) { ans += dp1[j]; } if (cnt0) ans *= cnt0 + 1; return ans.val(); } };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】