01背包的变形

01背包问题详解及代码实现

      我弟弟搞OI,问了我一道OI的题目,一看01背包,一做就是一上午。还是对01背包又忘记了。

      题目链接:点击打开链接

      

     01背包,之前博文有代码+讲解,再此就不赘余了。这道题目有主附件关系,但因为附件最多只有2件 ,所以主附件的组合最多4种,分别为

                                           

然后进行01背包,我们为了方便 把这4种方案放在结构体中;

伪代码:

void pack_01()
{
    for(int i=(背包容量);i>0;i--)
    {
        for(int x=0;x< (某一个主附件方案数);x++)
        {
            if(i- (某种方案的weight) >=0)//为了保证下面一行dp的下标为自然数
            dp[i]=max(dp[i],dp[i-(某种方案的weight)]+(某种方案的value));
        }
    }
}

你们或许会有一个问题,就是:这样怎么能够让属于同一类方案的只选择一个?

  dp数组里面,我们需要往前面进行状态转移!我们转移是4种方案,同时进行往前更新dp,这样转移是从上一个一大类方案中转移过来的。所以不会出现多个同为一个主件。

存数据,最好是结构体中,这样比较方便看;


/************************************************************/
/**         Reproduced please indicate the source          **/
/************************************************************/

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct item
{
    int v[4],w[4],c;//4种方案的花费, 价值;
};
item pack[65];
int ind[65];//下标索引,因为数据存的时候不是连续的;
int dp[3500];
void pack_01(int num,int n)
{
    for(int i=n; i>=0; i--)
    {
        for(int x=0; x<pack[num].c; x++)
        {
            if(i-pack[num].v[x]>=0)
                dp[i]=max(dp[i],dp[i-pack[num].v[x]]+pack[num].w[x]);
        }
    }
}
int main(void)
{
    int n,m,j;
    memset(dp,0,sizeof(dp));
    scanf("%d%d",&n,&m);
    j=1;
    n/=10;
    for(int i=1; i<=m; i++)
    {
        int v,p,q;
        scanf("%d%d%d",&v,&p,&q);
        v/=10;
        if(q==0)//如果是主件,存在 0 数组
        {
            pack[j].v[0]=v;
            pack[j].w[0]=p*v;
            pack[j].c=1;
            ind[i]=j;
            j++;
        }
        else//如果不是主件;
        {
            q=ind[q];
            if(pack[q].c==1)
            {
                pack[q].v[1]=pack[q].v[0]+v;
                pack[q].w[1]=pack[q].w[0]+v*p;
                pack[q].c=2;
            }
            else if(pack[q].c==2)//附件有两个
            {
                pack[q].v[2]=pack[q].v[0]+v;
                pack[q].w[2]=pack[q].w[0]+v*p;
                pack[q].v[3]=pack[q].v[1]+v;
                pack[q].w[3]=pack[q].w[1]+v*p;
                pack[q].c=4;
            }
        }
    }
    
    for(int i=1; i<j; i++)
        pack_01(i,n);
    printf("%d\n",dp[n]*10);
    return 0;
}




<think>好的,我现在需要帮用户了解01背包问题的变形及其动态规划解法。首先,我得回忆一下01背包的基础知识,再想想常见的变形有哪些。用户提到了动态规划,所以重点应该放在状态转移方程和代码实现上。 首先,01背包的基本问题是每个物品只能选或不选,而变形可能包括完全背包、多重背包,或者是混合背包。不过用户特别提到了01背包变形,所以可能需要考虑其他变化,比如二维费用、物品依赖、恰好装满、方案计数等。这些都属于01背包的扩展,但需要明确用户具体指的是哪一种变形。 接下来,用户需要解题思路和代码实现。动态规划的核心在于状态定义和转移方程。对于不同的变形,状态的定义可能会有调整。例如,如果是二维费用,状态可能从一维变成二维,表示两种不同的容量限制。如果是恰好装满,初始化条件会有所不同,通常将dp数组初始化为负无穷,只有dp[0]为0,这样只有恰好装满的情况才会被更新。 然后,我需要参考用户提供的引用内容。引用[1]提到将完全背包转化为01背包,引用[2]讨论混合背包问题的解法,引用[3]提到状态转移前的初始化技巧,引用[4]给出状态转移方程的例子。这些可能对解答有帮助,特别是关于如何处理不同背包问题的技巧。 用户的问题是关于01背包变形,所以可能需要结合这些引用中的方法。例如,完全背包转换为01背包可以通过二进制拆分,或者处理多重背包时使用二进制优化。不过用户可能更想知道的是如何调整01背包的动态规划来适应这些变形。 接下来,我需要组织回答的结构。首先介绍01背包的基本概念和动态规划解法,然后列举几种常见的变形,每种变形说明解题思路、状态转移方程的变化,并给出对应的代码示例。例如,处理恰好装满问题时,初始化的不同;处理二维费用时,状态数组的维度扩展。 还需要注意代码实现的正确性。例如,二维费用的背包问题需要双重循环遍历两种容量,而初始化可能需要更多的考虑。对于方案计数问题,状态转移可能不是取最大值,而是累加可能的方案数。 同时,要确保符合用户给出的格式要求,数学公式用$...$,独立公式用$$...$$,正确使用LaTeX语法,用中文回答,并在最后生成相关问题。引用用户提供的参考内容时,在适当的位置添加引用标识,如[^1]或[^2]。 最后,检查是否有遗漏的变形类型,确保覆盖用户可能关心的内容。例如,物品分组的依赖关系,或者求具体方案而非最大价值。这些都可能作为变形的一部分需要简要说明。</think>### 01背包问题变形及动态规划解法 #### 基础回顾 01背包问题的核心是:给定容量为$V$的背包和$N$件物品(第$i$件体积为$v_i$,价值为$w_i$),每件物品只能选0次或1次,求最大价值总和。 动态规划解法: - 定义状态$dp[j]$表示容量为$j$时的最大价值 - 状态转移方程: $$ dp[j] = \max(dp[j], dp[j - v_i] + w_i) \quad (j \geq v_i) $$ - 代码实现(空间优化版): ```python def knapsack_01(V, v, w): dp = [0] * (V + 1) for i in range(len(v)): for j in range(V, v[i]-1, -1): dp[j] = max(dp[j], dp[j - v[i]] + w[i]) return dp[V] ``` #### 常见变形与解法 --- ##### 变形1:恰好装满背包 **问题描述** 必须**恰好用完**背包容量,求最大价值(无法恰好装满时返回-1) **解题思路** - 初始化时$dp[0]=0$,其他$dp[j]=-\infty$ - 最终结果若为负数则说明无法恰好装满 **代码实现** ```python def exact_knapsack(V, v, w): dp = [-float('inf')] * (V + 1) dp[0] = 0 for i in range(len(v)): for j in range(V, v[i]-1, -1): dp[j] = max(dp[j], dp[j - v[i]] + w[i]) return dp[V] if dp[V] >= 0 else -1 ``` --- ##### 变形2:二维费用背包 **问题描述** 物品需要消耗两种资源(如重量+体积),背包有双重限制$V_1$和$V_2$ **解题思路** - 状态定义扩展为二维数组$dp[j][k]$ - 转移方程: $$ dp[j][k] = \max(dp[j][k], dp[j - v1_i][k - v2_i] + w_i) $$ **代码实现** ```python def two_dim_knapsack(V1, V2, v1, v2, w): dp = [[0]*(V2+1) for _ in range(V1+1)] for i in range(len(v1)): for j in range(V1, v1[i]-1, -1): for k in range(V2, v2[i]-1, -1): dp[j][k] = max(dp[j][k], dp[j - v1[i]][k - v2[i]] + w[i]) return dp[V1][V2] ``` --- ##### 变形3:方案计数 **问题描述** 求恰好装满背包的**方案总数**(不要求最大价值) **解题思路** - 将状态转移中的$\max$改为累加 - 初始化$dp[0]=1$,其他$dp[j]=0$ **代码实现** ```python def count_solutions(V, v): dp = [0]*(V+1) dp[0] = 1 for i in range(len(v)): for j in range(V, v[i]-1, -1): dp[j] += dp[j - v[i]] return dp[V] ``` --- ##### 变形4:分组背包 **问题描述** 物品属于不同的组,每组最多选1个物品 **解题思路** - 遍历顺序改为:组别→背包容量→组内物品 - 状态转移方程同01背包[^2] **代码片段** ```python for group in groups: for j in range(V, -1, -1): for item in group: if j >= item.v: dp[j] = max(dp[j], dp[j - item.v] + item.w) ``` --- #### 混合应用示例 当遇到同时包含普通物品、分组物品、二维费用物品的混合问题时,可通过分层处理不同约束条件,保持每层循环处理单一约束条件[^3]。此时二进制优化仍然是处理多重约束的有效方法[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值