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


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

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

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

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


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

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

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

    按照滚动数组得到的dp数组考虑,注意到物品顺序不影响答案,假设当前得到的是(f[0,dots,m]),删去物品(i)之后的答案是(g[1,dots,m]),则有当(j<v[i])时,(f[j]=g[j]),当(jgeq v[i])(f[j]=g[j]+g[j-v[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[i-1][j-c[i]])(dp[i-1][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的时候多带一个极差(leq3)的限制,后面发现原来是给的数据保证极差(leq 3),那就好做了,考虑最后选择的物品的集合是(S),最后要(sum_{iin S} v_ileq W),取(X=min_i(v_i)),式子变成(X|S|+sum_{iin S}(v_i-X)leq W),考虑个双重限制的背包:一个是(v_iin{0,1,2,3}),和第二维代价:每选一个物品代价是1,先对这个二维背包进行DP,然后再统计符合条件的答案。


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

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


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

    (K)个人,每个人有一个背包,容量都是(V)(N)件物品,现在要每个人都能恰好装满背包,并且任意两个人选的物品不完全相同,所有人价值之和的最大(Kleq 50,Vleq 5000,Nleq 200)

    发现相当于在求一个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++]);
        }
    }
    
  • 相关阅读:
    格式化你的git message
    git merge
    Git远程操作详解
    Limit
    EmailService
    RequestContextHolder getHttpServletRequest
    spring boot GlobalExceptionHandler @RestControllerAdvice @ExceptionHandler
    redis 的雪崩和穿透?
    FileUtil
    getWeekDay TimeUtil
  • 原文地址:https://www.cnblogs.com/yoshinow2001/p/14579490.html
Copyright © 2020-2023  润新知