首页 > 其他分享 >动态规划之背包问题

动态规划之背包问题

时间:2023-03-18 17:56:14浏览次数:40  
标签:背包 int -- 件物品 体积 物品 动态 规划

背包问题

1. 01背包问题

有 N件物品和一个容量是 V的背包。每件物品只能使用一次。第i件物品的体积是\(v_i\),价值是\(w_i\)。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

/* f[i][j] 表示只看前i个物品,总体积是j的情况下,总价值最大是多少
f[i][j] = max(1.,2.):
1. 不选第i个物品,f[i][j] = f[i - 1][j];
2. 选第i个物品,f[i][j] = f[i - 1][j - v[i]];
*/
int n,m; // n为背包数量,m为总体积
int f[N][N];
int w[N],v[N];
for(int i = 1;i <= n;i ++ )
	for(int j = 0;j <= m;j ++ )
	{
		f[i][j] = f[i - 1][j];
		if(j >= v[i]) f[i][j] = max(f[i][j],f[i - 1][j - v[i]] + w[i]);
	}
	int res = 0;
	for(int i = 0;i <= m;i ++ ) res = max(res,f[n][i]);
	
// 一维优化,由于f[i][j]计算中只用到了i-1的状态,因此可以将第一维优化掉
int f[N]; // f[j]表示体积为j时第i种物品的选择,i取决于外层循环
for(int i = 1;i <= n;i ++ )
	for(int j = m;j >= v[i];j -- ) //从大到小枚举,保证计算f[j]时使用的f[j-v[i]]没有被使用过,即为f[i-1][j-v[i]]
		f[j] = max(f[j],f[j - v[i]] + w[i]);
	int res = f[m];

2. 完全背包问题

有 N种物品和一个容量是 V的背包,每种物品都有无限件可用。第i种物品的体积是\(v_i\),价值是\(w_i\)。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

// f[i] 表示总体积是i的情况下,最大价值是多少
for(int i = 1;i <= n;i ++ )
	for(int j = v[i];j <= m;j ++ ) //与01背包对比,此处从小到大枚举j,即可保证计算f[j]的时候已经考虑了若干个第i个物品;
		f[j] = max(f[j],f[j - v[i]] + w[i]);
int res = f[m];
// 若考虑体积恰好为m时的最大值,则将除f[0]以外的都初始化为负无穷,01背包同理

3. 多重背包问题

有 N种物品和一个容量是V的背包。第i种物品最多有\(s_i\)件,每件体积是\(v_i\),价值是\(w_i\)。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

// f[i] 表示体积为i的情况下最大价值是多少

for(int i = 1;i <= n;i ++ )
	for(int j = m;j >= 0;j --)
		for(int k = 1;k <= s && k * v[i] <= j;k ++ )
			f[j] = max(f[j],f[j - k * v[i]] + k * w[i]);
int res = f[m];

/* 
优化1,适用于o(N*log(s)*V)复杂度,优化思路,将多重背包变成01背包问题
如果直接将s个物品拆成s份,时间复杂度仍然会很大,因此考虑二进制优化
s = 1 + 2 + 4 + ... + 2^k + s';其中k为最大的使2^k <= s的整数,这样使用logs个可以使用0或1次的物品即可凑出0到s所有的可能
*/
struct Good
{
	int v,w;
};
vector<Good> goods;
for(int i = 0;i < n;i ++ )
{
	for(int k = 1;k <= s;k *= 2)
	{
		s[i] -= k;
		goods.push_back({v[i] * k,w[i] * k});
	}
	if(s > 0) goods.push_back({v[i] * s,w[i] * s});
}

for(auto good:goods)
	for(int j = m;j >= good.v;j -- )
		f[j] = max(f[j],f[j - good.v] + good.w);
int res = f[m];

4. 混合背包问题

有 N种物品和一个容量是 V的背包。
物品一共有三类:
第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 \(s_i\)次(多重背包);
每种体积是 \(v_i\),价值是 \(w_i\)。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

// 就是将三种以上三种背包问题混合起来,根据s的数值判断是使用哪种转移方程
int v[N],w[N],s[N];
int f[N];
int n,m;
int main(){
    cin>>n>>m;
    int v,w,s;
    for(int i=1;i<=n;i++){
        cin>>v>>w>>s;
        if(!s)// s == 0 代表完全背包
            for(int j=v;j<=m;j++)
                f[j] = max(f[j],f[j-v]+w);
        else{
            if(s == -1) s = 1; // s = -1 代表01背包,将s置为1,转化为s=1的多重背包与其他多重背包一起进行二进制优化
            for(int k=1;k<=s;k*=2){
                for(int j=m;j>=v*k;j--)
                    f[j]=max(f[j],f[j-k*v]+k*w);
                s-=k;
            }
	    if(s)
		for(int j=m;j>=s*v;j--)
			f[j]=max(f[j],f[j-s*v]+w*s);
        }
    }

5. 二维费用的背包问题

有 N件物品和一个容量是V的背包,背包能承受的最大重量是 M。每件物品只能用一次。体积是 \(v_i\),重量是\(m_i\)价值是\(w_i\)。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,总重量不超过背包可承受的最大重量,且价值总和最大。

/*
f[i][j] 体积为i重量为j的最大价值
01背包,体积和重量从大到小枚举
*/

for(int i = 1;i <= n;i ++ )
	for(int j = V;j >= v[i];j -- )
		for(int k = M;k >= m[i];k -- )
			f[j][k] = max(f[j][k],f[j - v[i]][k - m[i]] + w[i]);
int res = f[V][M];

6. 分组背包问题

有N组物品和一个容量是V的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是 \(v_{ij}\),价值是\(w_{ij}\),其中 i是组号,j是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

// f[j] 表示最大体积为j时的最大价值
// v[i][j]表示第i组第j个
for(int i = 0;i < n;i ++ )
	for(int j = m;j >= 0;j -- )
		for(int k = 0;k < s;k ++ )
			if(j >= v[i][k]) f[j] = max(f[j],f[j - v[i][k]] + w[i][k]);
int res = f[m];

7. 有依赖的背包问题

有 N个物品和一个容量是 V的背包。物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如下图所示:
image
如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。
每件物品的编号是i,体积是\(v_i\),价值是\(w_i\),依赖的父节点编号是\(p_i\)。物品的下标范围是1…N。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

/*
f[i][j]表示以第i个节点为根节点的子树在体积为j的条件下的最大价值
将其考虑成分组背包问题,子节点对于每个可选的体积是一个组
*/

void dfs(int u)
{
	for(int i = h[u];i != -1;i = ne[i])
	{
		int son = e[i];
		dfs(son);
		for(int j = m - v[u];j >= 0;j -- )
			for(int k = 0;k <= j;k ++ )
				f[u][j] = max(f[u][j],f[u][j - k] + f[son][k]);
	}
	for(int i = m;i >= v[u];i -- ) f[u][i] = f[u][i - v[u]] + w[u];
	for(int i = 0;i < v[u];i ++ ) f[u][i] = 0;
}
dfs(root);
int res = f[root][m];

8. 背包问题求方案数

有N件物品和一个容量是 V的背包。每件物品只能使用一次。第i件物品的体积是\(v_i\),价值是 \(w_i\)。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
求最优选法的方案数

/*
f[i] 体积为i的情况下的最大价值
g[i] 体积为i的情况下的方案数
*/
// 初始化
g[0] = 1;
for(int i = 1;i <= m;i ++ ) f[i] = -INF;
for(int i = 0;i < n;i ++ )
	for(int j = m;j >= v[i];j -- )
	{
		int t = max(f[j],f[j - v[i]] + w[i]);
		int s = 0;
		if(t == f[j]) s += g[j];
		if(t == f[j - v[i]] + w[i]) s += g[j - v];
		f[j] = t,g[j] = s;
	}
// 求解最优解
int maxw = 0;
for(int i = 0;i < n;i ++ ) maxw = max(maxw,f[i]);
int res = 0;
for(int i = 0;i <= m;i ++ )
	if(maxw == f[i])
		res += g[i]

9. 求背包问题的方案

有N件物品和一个容量是 V的背包。每件物品只能使用一次。第i件物品的体积是\(v_i\),价值是\(w_i\)。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出 字典序最小的方案。这里的字典序是指:所选物品的编号所构成的序列。物品的编号范围是 1…N。

/*
f[i][j] 表示考虑前i个物品,最大体积为j的最大价值
*/
for(int i = n; i >= 1;i --)
	for(int j = 0;j <= m;j ++ )
	{
		f[i][j] = f[i + 1][j];
		if(j >= v[i]) f[i][j] = max(f[i][j],f[i + 1][j - v[i]] + w[i]);
	}
	int vol = m;
	// 判断是从哪个方案转移过来
	for(int i = 1;i <= n;i ++ )
		if(f[i][vol] == f[i + 1][vol - v[i]] + w[i])
		{
			cout << i << ' ';
			vol -= v[i];
		}

标签:背包,int,--,件物品,体积,物品,动态,规划
From: https://www.cnblogs.com/zhiao/p/17231277.html

相关文章

  • React 实现 动态加载组件
    React实现动态加载组件import{Button}from'antd'importReact,{useState,lazy,Suspense}from'react'//这个地方动态加载组件constItem=lazy(()=>i......
  • 动态路由协议
    1动态路由的概述​虽然静态路由在有些时候很有用,但是是必须是手动操作每条路由条目,对于大型网络来说经常改变的情况,配置静态路由工作量非常繁重,因此使用动态路由是必要的。......
  • Spring Study -lesson11-动态代理扩展-2023-03-18
    一个动态代理接口类,可以作为工具接口,便于不同程序公共使用packagecom.feijian.Demo02;importcom.feijian.Demo.Rent;importcom.sun.corba.se.impl.ior.OldJIDLObje......
  • Spring Study -lesson11-动态代理(反射机制)-2023-03-18
    第一:接口类(增删改查)packagecom.feijian.Service;publicinterfaceUserService{publicvoidaddUser();publicvoidupdateUser();publicvoiddelet......
  • 算法随想Day41【动态规划】| LC139-单词拆分
    LC139.单词拆分dp[i]含义:字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词遍历顺序:如题说,是拆分为一个或多个在字典中出现的单词,所以这是完......
  • 代码随想录算法训练营Day45 动态规划
    代码随想录算法训练营代码随想录算法训练营Day45动态规划|70.爬楼梯(进阶)322.零钱兑换70.爬楼梯(进阶)题目链接:70.爬楼梯(进阶假设你正在爬楼梯。需要n 阶你才能......
  • 动态开点线段数 & 线段数合并学习笔记
    动态开点线段树使用场景\(4\timesn\)开不下。值域需要平移(有负数)。什么时候开点显然,访问的节点不存在时(只会在修改递归时开点)。trick区间里面有负数时,\(mid=......
  • 跟艾文学编程《零基础入门学Python》(01)基于Plotly的动态可视化绘图
    作者:艾文,计算机硕士学位,企业内训讲师和金牌面试官,公司资深算法专家,现就职BAT一线大厂。 内容:跟艾文学编程《零基础入门学Python》目标plotly基础概念介绍plotly......
  • redis的简单动态字符串
    概念redis在c的基础上编写,但是redis的许多数据结构是不同于c的数据结构。redis的字符串表示是利用自己构建的SDS(简单动态字符串)作为默认字符串表示的。而c默认的字符......
  • 11-列表动态渲染
    题目:请补全JavaScript代码,将预设代码中的"people"数组渲染在页面中。实现下面的列表:牛油1号20岁牛油2号21岁牛油3号19岁答案:`<script>varpeople......