[日常摸鱼]一些DP题(2)各种背包——01背包/二维背包/背包前K优解

https://codeforces.com/problemset/problem/19/B

Bob拿着n件商品在收银台付款,扫描第i件商品需要ti的时间,第i件的价格为ci,在扫描的时候可以选择偷走一些商品,偷走一个商品需要1个单位的时间,问最少花多少钱能获得所有商品。n2000,ti2000

相当于把商品划分成两个集合S,T,满足|T|Sti,使得Sci最小,左边的式子稍微变形会得到Tti+|T|=T(ti+1)ti,这就很像背包了:反着考虑获得哪些物品不要花钱,总容量为ti,选择一件物品不花钱得到的代价是ti+1,这样一个01背包问题,但是复杂度会达到O(nt2)=O(n3),接受不了。

不过顺着这个背包的思路继续想,选择花钱买一件物品相当于多获得一个ti+1的体积,对应的付出ci的代价,最终目标是获得所有物品,即Sti+1n,于是又是一个背包:选择花钱购买一些物品,使得这些物品的体积之和超过n,求最小的代价。同样的问题又来了,这样做背包的体积上界是多少?如果还是O(n2)级别的话这个优化就没什么用了:仔细想一下上界不会很大,在一系列决策之后如果当前的ti+已经超过了n,那后面的一定不会继续选择购买,所以最大的情况一定是从一个小于n的体积跨越到一个大于n的体积,对应的上界就是n1+(vmax+1)=n+vmax了。


https://www.luogu.com.cn/problem/P4141

背包问题变形,n个物品,需要回答如果没有第i个物品的时候,恰好装满容量为x=1,m的背包需要多少代价?n,m2000

原问题是dp[i][j]=dp[i1][j]+dp[i1][jv[i]]dp[0][0]=1,暴力做是直接O(n2m)的,先处理处没有第一个物品的答案,然后考虑如何给dp删除一个物品和添加物品。

按照滚动数组得到的dp数组考虑,注意到物品顺序不影响答案,假设当前得到的是f[0,,m],删去物品i之后的答案是g[1,,m],则有当j<v[i]时,f[j]=g[j],当jv[i]f[j]=g[j]+g[jv[i]],于是就能从小到大反推出g[],这样每一次只需要O(m)的代价计算删除和加入一个物品的答案。

rep(i,2,n){
rep(j,0,w[i]-1)g[j]=f[j];
rep(j,w[i],m)g[j]=(f[j]-g[j-w[i]]+10)%10;
rep(j,0,m)f[j]=g[j];
for(int j=m;j>=w[i-1];j--)upd(f[j],f[j-w[i-1]]);
rep(j,1,m)printf("%c",f[j]+'0');
puts("");
}

https://www.luogu.com.cn/problem/P1877

01背包变形,dp[i][j]dp[i1][jc[i]]dp[i1][j+c[i]]两个转移。


https://www.luogu.com.cn/problem/P1509

二维背包变形,两个代价(rmb和rp)以及两个收益(在泡到最多MM的前提下时间最小),可以考虑两个dp数组,在MM最多的前提下再比较第二维,或者像我在实现的时候直接开个struct来存dp,重载一个比较函数。


https://www.luogu.com.cn/problem/P3985

二维背包变形,一开始理解错题意,以为是DP的时候多带一个极差3的限制,后面发现原来是给的数据保证极差3,那就好做了,考虑最后选择的物品的集合是S,最后要iSviW,取X=mini(vi),式子变成X|S|+iS(viX)W,考虑个双重限制的背包:一个是vi0,1,2,3,和第二维代价:每选一个物品代价是1,先对这个二维背包进行DP,然后再统计符合条件的答案。


https://www.luogu.com.cn/problem/P1455

并查集维护连通块,然后直接对每一个联通块01背包


https://www.luogu.com.cn/problem/P1858

K个人,每个人有一个背包,容量都是VN件物品,现在要每个人都能恰好装满背包,并且任意两个人选的物品不完全相同,所有人价值之和的最大K50,V5000,N200

发现相当于在求一个01背包要求完全装满的前K大值,前K大的处理一般是把普通的DP式子转化成一个单调队列来维护,转移变成O(k)地归并状态。

rep(i,1,n)per(j,V,v[i]){
tmp.clear();
for(auto itr:f[j])tmp.pb(itr);
f[j].clear();
int p=0,q=0;
while((p<tmp.size()||q<f[j-v[i]].size())&&(p+q<=k)){
int sp=tmp.size(),sq=f[j-v[i]].size();
if(q>=sq||(p<sp&&q<sq&&tmp[p]>w[i]+f[j-v[i]][q]))f[j].pb(tmp[p++]);
else f[j].pb(w[i]+f[j-v[i]][q++]);
}
}
posted @   yoshinow2001  阅读(85)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示