多重背包(单调队列优化)

用单调队列可以大大提高效率

#include<stdio.h>
#include<string.h>
#define MAXN 105
struct Queue
{
    int num,value;
}que[250005];
int head,tail;
int v[MAXN],w[MAXN],c[MAXN];
int dp[250005];
void enqueue (int x,int y)
{
    while (head<=tail && que[tail].value<y) tail--;
    que[++tail].num=x;que[tail].value=y;
}
int main()
{
    int i,j,d,sum,n,tempsum;
    while (scanf("%d",&n) && n>=0)
    {
        sum=0;
        for (i=1 ; i<=n ; ++i)
        {
            scanf("%d%d",&v[i],&c[i]);
            w[i]=v[i];
            sum+=w[i]*c[i];
        }
        tempsum=sum;
        sum/=2;
        for (i=1 ; i<=sum ; ++i) dp[i]=0;
        for (i=1 ; i<=n ; ++i)
        {
            if (c[i] > sum/w[i]) c[i]=sum/w[i];
            for (d=0 ; d<w[i] ; ++d)
            {
                head=1;tail=0;
                for (j=0 ; j<=(sum-d)/w[i] ; ++j)
                {
                    enqueue(j , dp[j*w[i]+d]-j*v[i]);
                    while (que[head].num<j-c[i] && head<=tail) head++;
                    dp[j*w[i]+d]=que[head].value+j*v[i];
                }
            }
        }
        printf("%d %d/n",tempsum-dp[sum],dp[sum]);
    }
    return 0;
}


### 单调队列优化多重背包问题的Python实现 对于多重背包问题,当物品数量较大时,朴素解法的时间复杂度过高。为了提高效率,可以采用单调队列优化求解过程[^2]。 #### 原理说明 在处理第`i`种物品时,设该物品的价值为`v[i]`,体积为`w[i]`,最多可以选择的数量为`s[i]`。假设当前考虑容量范围是从`j-w[i]*k`到`j`(其中`0≤k<s[i]+1`),那么在这个范围内选择若干件此物品所获得的最大价值可以通过动态规划的状态转移方程计算得出: \[ dp[j]=\max(dp[j],dp[j-k*w[i]]+k*v[i]) \] 这里存在重复子结构的特点,即随着`k`增加,新的状态总是基于旧的状态更新而来。利用这一点特性,引入滑动窗口最大值的思想——每当遍历一个新的位置`j`时,只需要关注那些能够影响当前位置决策的有效历史信息即可;而这些有效的历史信息正好可以用一个维护着最近若干个元素及其对应索引的双端队列(`deque`)表示出来。 具体来说就是保持一个按顺序存储满足条件的位置列表,并且保证这个列表内的每一个成员都严格优于它后面的成员(这里的优劣关系取决于题目中的定义)。这样做的好处在于每次查询最优解的时候只需访问一次队首元素就能得到答案,从而大大降低了时间开销。 #### Python代码示例 下面给出一段具体的Python代码用于解决上述提到的问题: ```python def multi_pack(n, V, v, w, s): from collections import deque f = [float('-inf')] * (V + 1) f[0] = 0 for i in range(1, n + 1): q = deque() for j in range(V // w[i]): cur_j = min(j * w[i], V) # 清除不在窗口内的元素 while q and q[0][0] < j - s[i]: q.popleft() # 更新f[cur_j] if not q or q[0][1] >= f[cur_j]: f[cur_j] = max(f[cur_j], q[0][1] if q else 0) + v[i] * ((cur_j % w[i] == 0) ^ bool(q)) # 维护单调递减队列 while q and q[-1][1] <= f[cur_j]: q.pop() q.append((j, f[cur_j])) return max(f[V//w[i]*w[i]:]) # 测试数据 n = 3 # 物品数目 V = 8 # 背包总容量 v = [6, 3, 5] # 各类物品单位重量下的价值 w = [2, 2, 3] # 各类物品每件占用的空间大小 s = [4, 2, 3] # 对应种类物品可选数量上限 print(multi_pack(n,V,v,w,s)) ``` 这段程序实现了通过单调队列的方式快速找到每个阶段的最佳方案并最终返回整个过程中所能达到的最大收益值。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值