背包问题(介绍+例题+代码+注解)

目录

 介绍:

一、01背包

题目描述

输入描述:

输出描述:

代码:

 二、完全背包

题目描述

输入描述:

输出描述:

代码: 

三、多重背包

题目描述

输入描述:

输出描述:

代码: 

四、背包问题 

题目描述

输入描述:

输出描述:

代码: 

 五、变1

代码: 

 六、变2

代码: 

 七、变3

代码:


 介绍:

背包问题是一个经典的组合优化问题,它的目标是在给定的一组物品中选择一些物品放入背包中,使得这些物品的总价值最大化,同时限制背包的容量。

背包问题有多种变体,其中最常见的是0-1背包问题。在0-1背包问题中,每个物品要么完整地放入背包中,要么不放入,不存在放入一部分的情况。每个物品都有对应的价值和重量,背包有一个固定的容量限制。问题在于选择哪些物品放入背包中,使得总价值最大化,同时不超过背包的容量。

解决背包问题的方法包括动态规划、贪心法和回溯法。动态规划是最常用的解决方法,其基本思想是将问题划分为子问题,并使用一个二维数组来保存子问题的最优解。贪心法则是每次选择当前最有利的物品放入背包,但不一定能得到最优解。回溯法则是穷举所有可能的解,并逐步剪枝,但在处理大规模问题时效率较低。

背包问题具有广泛的应用,例如资源分配、排课问题、投资组合优化等。

一、01背包

题目描述

你有一个背包,最多能容纳的体积是V。

现在有n个物品,第i个物品的体积为v ,价值为w。

(1)求这个背包至多能装多大价值的物品?

(2)若背包恰好装满,求至多能装多大价值的物品?

输入描述:

第一行两个整数n和V,表示物品个数和背包体积。

接下来n行,每行两个数v和w,表示第i个物品的体积和价值。

1≤n,V,vi,wi≤10001 \le n,V,v_i,w_i \le 10001≤n,V,vi​,wi​≤1000

输出描述:

输出有两行,第一行输出第一问的答案,第二行输出第二问的答案,如果无解请输出0。

代码:

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	int n, v;
	cin >> n >> v;
	int a[1005],b[1005];
	int f[1005][1005];//表示第i个物品装满j体积
    memset(f, -0x3f, sizeof(f));
	for (int i = 1; i <= n; i++)
		cin >> a[i] >> b[i];
	int ans = 0;
    f[0][0]=0;//0个物体可以装满0体积
	for(int i=1;i<=n;i++)
		for (int j = 0; j <= v; j++)
		{
            if(a[i]<=j)//体积大于该物体体积,才可以装物品
			    f[i][j] = max(f[i - 1][j], f[i - 1][j - a[i]] + b[i]);
            else//不装该物体
                f[i][j]=f[i-1][j];
			if (ans < f[i][j])//求最大价值
				ans = f[i][j];
		}
	cout << ans << endl;
    if(f[n][v]>0)
	cout << f[n][v] << endl;
    else
        cout<<0;
}

 二、完全背包

题目描述

你有一个背包,最多能容纳的体积是V。

现在有n种物品,每种物品有任意多个,第i种物品的体积为vi​ ,价值为wi​。

(1)求这个背包至多能装多大价值的物品?

(2)若背包恰好装满,求至多能装多大价值的物品?

输入描述:

第一行两个整数n和V,表示物品个数和背包体积。

接下来n行,每行两个数v​和w,表示第i种物品的体积和价值。

1≤n,V≤10001 \le n, V \le 10001≤n,V≤1000

输出描述:

输出有两行,第一行输出第一问的答案,第二行输出第二问的答案,如果无解请输出0。

代码: 

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	long long n, V;
	cin >> n >> V;
	long long dp1[1010], dp2[1010];
	memset(dp1, 0, sizeof(dp1)), memset(dp2, 0, sizeof(dp2));
	for (int i = 1; i <= n; i++)
	{
		long long v, w;
		cin >> v >> w;
		for (int j = 0; j <= V; j++)
		{
			if (j >= v)
			{
				dp1[j] = max(dp1[j], dp1[j - v] + w);
				if (j % v == 0 || dp2[j-v] != 0) //该体积为该物体体积的倍数或者该体积减去物体体积不为0
                    dp2[j] = max(dp2[j], dp2[j - v] + w);//塞这个物品,原先该体积的价值和减去该体积的价值加塞下该物品的价值谁大
			}
		}
	}
	cout << dp1[V] << endl;
	cout << dp2[V] << endl;
}

三、多重背包

题目描述

有 n 种物品,第 i 种物品有 x​ 个,每一个物品重量为 w ,价值为 v,现有一个承重能力为 T 的背包,在不超过承重能力的情况下,背包种最多能装多少价值的物品。

输入描述:

第一行输入两个整数 n,T(1≤n,T≤100)n,T(1\le n,T \le 100)n,T(1≤n,T≤100) ,代表物品种数和背包承重能力。

接下来 nnn 行,每行三个整数 xi,wi,vi(1≤xi,wi≤20,1≤vi≤200)x_i,w_i,v_i(1\le x_i,w_i\le 20,1\le v_i \le 200)xi​,wi​,vi​(1≤xi​,wi​≤20,1≤vi​≤200) 描述一个物品,分别代表物品的个数、物品的重量、物品的价值。

输出描述:

输出一行一个整数,表示在不超过承重能力的情况下,背包物品的最大价值。

代码: 

#include<iostream>
using namespace std;
int main()
{
	int n, t;
	cin >> n >> t;
	int f[105][105];//表示第i个物品装j体积
	for (int i = 1; i <= n; i++)
	{
		int x, w, v;
		cin >> x >> w >> v;
		for (int j = 0; j <= t; j++)
		{
			for (int k = 0; k <= x; k++)//表示放k个该物品
				if(j>=k*w)//要该体积大于k个物品体积才能放那么多个物品
				f[i][j] = max(f[i][j], f[i - 1][j - k * w] + k*v);
		}
	}
	cout << f[n][t];
}

四、背包问题 

题目描述

牛妹家里有n个物品,第i个物品体积为v​,价值为w​。牛妹有一个容积无穷大的背包,背包可以装任意多的物品,没有限制。
牛妹要去深山中度假,为了在旁人眼中显得自己准备得很充分,牛妹想在背包中装入总体积不小于V的一些物品。如果装入的物品过于贵重,路上如果遇到强盗、劫匪、山贼等就很亏。所以牛妹想知道总体积不小于V的前提下,物品的总价值最小是多少。

输入描述:

第一行两个整数n,V\mathit n,Vn,V,表示物品的数量和总体积需求。

接下来n\mathit nn行,每行两个整数vi,wiv_i,w_ivi​,wi​,表示第i\mathit ii种物品的体积和价值。保证所有物品总体积不小于V\mathit VV。

1≤n≤200,1≤V≤106,1≤vi≤106,1≤wi≤2001 \leq n \leq 200, 1 \leq V \leq 10^6, 1 \leq v_i \leq 10^6, 1 \leq w_i \leq 2001≤n≤200,1≤V≤106,1≤vi​≤106,1≤wi​≤200。保证所有物品总体积不小于V\mathit VV。

输出描述:

输出一个整数,表示答案。

代码: 

#include<iostream>
using namespace std;
int v[1000100], w[1000100];
int f[1000100];//j价值可以装的最大体积
int main()
{
	int n,V;
	long long sum = 0;
	cin >> n >> V;
	for (int i = 1; i <= n; i++)
	{
		cin >> v[i] >> w[i];
		sum += w[i];//总价值
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = sum; j >= w[i]; j--)
			f[j] = max(f[j], f[j - w[i]] + v[i]);
	}
	for (int i = 1; i <= sum; i++)
		if (f[i] >= V)//找到大于等于V体积的最小价值
		{
			cout << i << endl;
			break;
		}
}

 五、变1

代码: 

#include<iostream>
using namespace std;
int main()
{
	int n, m;
	cin >> n >> m;
	int v[30], p[30];
	for (int i = 1; i <= m; i++)
	{
		cin >> v[i] >> p[i];
	}
	long long dp[30010] = { 0 };//当存体积为j时的,价值最大值
	for(int i=1;i<=m;i++)
		for (int j = n; j >= v[i]; j--)//当存到第i个时,花费为j时价值的最大值
		{
			dp[j] = max(dp[j], dp[j - v[i]] + p[i] * v[i]);//不选取i则继承存到第i-1个时的最大值,选取i则最大值更新为减去第i个花费时的最大值dp加上第i个的价值,即dp[j - v[i]] + p[i] * v[i]
		}
	cout << dp[n] << endl;
}

 六、变2

代码: 

#include<iostream>
#include<cstring>
using namespace std;
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n, s;
		cin >> n >> s;
		long long  f[500000], q[500000];//f记录j体积的最高战力,q记录j体积的精灵数量
		memset(f, 0, sizeof(f)), memset(q, 0, sizeof(q));
		for (int i = 1; i <= n; i++)
		{
			int w, v;
			cin >> w >> v;
			for (int j = s; j >= w; j--)
			{
				if (f[j] < f[j - w] + v)//装该精灵战力更高,则更新
				{
					f[j] = f[j - w] + v;
					q[j] = q[j-w]+1;
				}
			}
		}
		cout << f[s] << " "<<q[s]<<endl;
	}
}

 七、变3

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int a[105], f[25005];//f[i]表示价值i是否可以表示
int main()
{
	int t;
    cin>>t;
	while (t--)
	{
		int n;
		cin >> n;
		int ans = n;
		for (int i = 1; i <= n; i++)
			cin >> a[i];
		sort(a + 1, a + n + 1);//从小到大排序
		memset(f, 0, sizeof(f));//清空为0
		f[0] = 1;
		for (int i = 1; i <= n; i++)
		{
			if (f[a[i]] == 1)//a[i]该数值可以被之前表示,则ans--,这个票不需要用到
			{
				ans--;
				continue;
			}
			for (int k = a[i]; k <= a[n]; k++)//a[i]该数值不可以被表示,则从该数值到最大值,将加上a[i]后可以表示的数赋值为1
			{
				f[k] = f[k] || f[k - a[i]];
			}
		}
		cout << ans << endl;
	}
}

 

  • 25
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值