基础背包问题&自定义二维数组

题目描述

01背包问题

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 ii 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N,V≤1000
0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

8

代码

#include <iostream>

void func(int **dynamic, int *V, int *W, int v, int w) //函数这里是实现该问题的关键
{

	for (int i1 = 0; i1 <= v; i1++)
	{
		for (int i2 = 0; i2 <= w; i2++)
		{
			if (i1 == 0 || i2 == 0)
			{
				dynamic[i1][i2] = 0;
				continue;
			}
			if (W[i1 - 1] > i2)
			{
				dynamic[i1][i2] = dynamic[i1 - 1][i2];
			}
			else
			{
				dynamic[i1][i2] = ((dynamic[i1 - 1][i2]) > (dynamic[i1 - 1][i2 - W[i1 - 1]] + V[i1 - 1]) ? dynamic[i1 - 1][i2] : (dynamic[i1 - 1][i2 - W[i1 - 1]] + V[i1 - 1]));
			}
		}
	}
	return;
}

int main()
{

	int v = 0, w = 0;
	std::cin >> v >> w; //v是物品总数,w是容量,或者说最大承重

	int *V = new int[v];
	int *W = new int[v];

	for (int i = 0; i < v; i++)
		std::cin >> W[i] >> V[i]; //V[]是物品价值数组,W[]是物品体积(或者说重量)数组

	int **dynamic = new int *[v + 1]; //这段是尝试使用自定义二维数组
	for (int i = 0; i <= v; i++)
	{
		dynamic[i] = new int[w + 1];
	}

	func(dynamic, V, W, v, w);
	std::cout << dynamic[v][w];

	return 0;
}

解题思路

image-20210616190637735

  1. 构建一个从0个物品到N个物品,从0容量到V容量的二维数组(如上图),其中每个下标为[ni][vi]的位置,表示的是在前ni个物品,容量为vi的情况时,所取得的最优解(即所能容纳的最大价值)

  2. 首先,第零行和第0列,表示的时前0个物品或者容量为0时的最大价值,全为0

  3. 然后,填充后面的位置时,考虑当前的vi所能是否能容纳第ni个物品,如果能容纳,那么有两个选择,放和不放

    1. 选择不放时,该处的值为二维数组中上一行的同位置所列出的数,即,当前位置[ni][vi]的值等于[ni-1][vi]的值(因为容积vi相同的情况下,如果不放这第ni件物品,那么当前位置的值仍是前ni件物品时的最优解)

    2. 选择放,那么放了之后,容积还剩vi-V[ni],那么,当前位置的最优解为第ni个物品的价值value[ni]再加上[ni-1][vi-V[ni]]的值

    3. 选: f[ni][vi] = value[ni] + f[ni-1][vi-V[ni]]
      不选:f[ni][vi] = f[ni-1][vi]

      两者取最大值,即f[ni][vi]处的最优解

  4. 综上,可得解,列出对应的循环来填充该二维数组,最后取[vi][ni]处的值,即为该问题的解

视频参考:【动态规划】背包问题_哔哩哔哩_bilibili

扩展1:完全背包问题

题目条件

同上,额外增加一个条件:每种物品数量不限

两个代码其实只有一句不同(注意下标)

dynamic[i1 - 1][i2 - W[i1 - 1]] + V[i1 - 1])	//01背包问题
    dynamic[i1][i2 - W[i1 - 1]] + V[i1 - 1])	//完全背包问题

核心部分类比上面的序号3里面的内容,就基本上理解了,原理还是同容积下第[ni-1]个物品的最优解已经求出来,

不同的是,在该问题下还要注意的一点是:同一行中,当前关注位置之前的最优解也已求出,所以只用判断当前能不能继续添加,关于加完后的[ni][vi-V[ni]]位置的最优解已经求出,直接代入即可

扩展2:多重背包问题(没解出来)

题目条件

类似于完全背包问题,但是每种物品最多有si件

给我做破防了

找到的题解的思路是,把每件物品的数量拆开,拆成多个只有一个的物品,然后按照01背包问题来做

我的思路是:再加一个一维数组N[]来表示一个数量队列,然后每次使用了一次物品之后数量减一,之后就是=执行01背包问题的代码,但是实现过程中遇到各种各样的问题,勉强能找出问题所在,但是想一个解决办法不行一个,一直到破防

以下是错误的核心部分代码,感兴趣的大佬可以帮忙看看(应该不会有大佬愿意看这么无聊的玩意吧),

	for (int i1 = 0; i1 <= v; i1++)
	{
		int i=N[i1-1];
		N[i1-1]*=W[i1-1];
		for (int i2 = 0; i2 <= w; i2++)
		{
			if (i1 == 0 || i2 == 0)
			{
				dynamic[i1][i2] = 0;
				continue;
			}
			if(N[i1-1]==0){dynamic[i1][i2] = ((dynamic[i1][(W[i1-1]*i+1)>101?101:((W[i1-1]*i)+1)]) > (dynamic[i1][i2-W[i1-1]]+V[i1-1]) ? (dynamic[i1][(W[i1-1]*i+1)>101?101:((W[i1-1]*i)+1)]):(dynamic[i1][i2-W[i1-1]]+V[i1-1]));continue;}

			if (W[i1 - 1] > i2)	dynamic[i1][i2] = dynamic[i1 - 1][i2];
			else
			{
				if(N[i1-1]!=0)	dynamic[i1][i2] = ((dynamic[i1-1][i2]) > (dynamic[i1][i2-W[i1-1]]+V[i1-1]) ? dynamic[i1-1][i2] : ((N[i1-1]--),(dynamic[i1][i2-W[i1-1]]+V[i1-1])));
				else {dynamic[i1][i2] = ((dynamic[i1-1][i2]) > (dynamic[i1-1][i2-W[i1-1]]+V[i1-1]) ? dynamic[i1-1][i2] : (dynamic[i1-1][i2-W[i1-1]]+V[i1-1]));}
			}
		}
	}

其他无关的(比扩展2有用一点)

关于动态数组,本质上就是指针指来指去,搞清楚指向位置是变量还是变量的指针,就基本上没问题了

然后,我敲代码的过程中,还遇到一个问题,就是关于普通的二维数组甚至高维数组作为参数传递给函数是,是需要指明除第一维之外的其他维度的具体值,即:

int a[5][10]={0}
则定义函数时,需要指明第二维有多长,即:
void func(int a[][10]);

关于该问题,与c++中的二维数组的形成方式有关,大概是,二维数组存储时仍是以一维数组的形式存储,数组名只是提供一个指向该数组内存空间的指针,如果不说明第二维有多长,那么在函数中调用该数组时,程序就无法理解比如a[2][3]要找的是哪个位置的值

其他无关的里面是我半瓶水晃荡的结果,如有错误,还请大佬们批评指正

以上

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值