如何用动态规划解决0-1背包问题(C语言实现)

2024-06-23 20:08

本文主要是介绍如何用动态规划解决0-1背包问题(C语言实现),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对于一组不同重量、不可分割的物品,我们需要选择一些装入背包,在满足背包最大重量限制前提下,背包中物品总重量的最大值是多少?假设此时是5个物品,2,2,4,6,3,然后背包最大承载两是9.

假如我们使用回溯算法解决该问题, 代码如下

int maxW = 0; //最大重量
int n = 5; //物品数目
int w = 9; // 背包最大重量
int weight[] = {2,2,4,6,3};// 物品重量,2,2,4,6,3void rucksack(int i, int cw)
{if ( cw == w || i == n ){if ( cw > maxW ) maxW= w;return ;}rucksack(i + 1, cw);//不装第i个物品if ( cw + weight[i] <= w){ //如果装的下rucksack(i + 1, cw + weight[i]);}
}

如果将代码执行过程产生状态画成树,我们可以发现对于不加入物品2的选择f(1,0)和加入物品2的选择f(1,2), 在下一个选择时,他们有一个相同的状态,f(2,2)。

2013053-3c6bd0889148f31f.png
递归树

虽然得到它的过程不同,但是从这往后,大家都是一样的。如同你走迷宫,走到一个地方,你发现墙上有一张纸写着,"此路不通",你就可以不用白费力气去探索了。因此,如果一个问题解决时存在重复子问题,我们可以通过记忆化的方式,避免重复运算,提高计算效率。

从动态规划的角度,我们可以将整个求解过程分为n个阶段,每个阶段都需要决策是否需要将物品放到背包中。每个物品的决策后,背包中物品的重量就有会有种情况,也就是达到了不同的状态,也就是递归树中的不同节点。在每一个层中,我们只记录不同的状态(比如说上图的第2层的两个f(2,2)就可以合并成一种情况,当然第四层就更多了)。这样一来,我们就保证了每一层的状态数就不会超过w个(w是背包的承载重量)。这种合并操作就可以认为是一种记忆化。

我们先用一个二维数组来记录每层可以达到的不同状态

2013053-00dfa761bf2c2fdb.png
初始状态

考场重量为2的物品后,会出现两种状态,0和2。

2013053-acaaf7424a72086c.png
状态1

在上一个状态的基础再考察一个重量为2的物品,会有三种状态,一直不选择,先不选再选2,两次都选2.

2013053-7e4c781d20f6bfea.png
状态2

继续考察重量为4的情况时,会出现五种情况,其中重量为4可能来源是0+0+4,2+2+0,这种重复状态就被合并成一种状态,因此减少了计算量。

2013053-d254ae295e846f64.png
状态3

最终状态如下

2013053-9e65b0378f35d859.png
最终状态

将上面的思考过程翻译成代码就是,

int rucksackDp(int *weight, int num, int w)
{//先定义一个二维数组int **status = (int **)malloc( sizeof(int *) * num);//初始化数组for (int i = 0; i < num; i++){status[i] = (int *)calloc( w+1, sizeof(int));}//初始化第一行的值status[0][0] = 1; //不放if ( weight[0] < w){status[0][weight[0]] = 1; //放}//从第二个开始考虑for (int i = 1; i < num; i++){//不放的情况,直接将上面的结果复制给当前for (int j = 0; j <= w; j++){if (status[i-1][j] == 1)  status[i][j] = status[i-1][j];}//上面循环可以写成//memcpy(status[i], status[i-1], sizeof(int) * num);//放: 在上一个状态基础上, 将增加后的重量对应位置设置为1for ( int j = 0; j <= w -weight[i]; j++){if ( status[i-1][j] == 1) status[i][j+weight[i]] = 1;}}//输出结果for ( int i = w; i >= 0; --i){if (status[n-1][i] == 1) return i;}return 0;
}

上面我们使用的是二维数组用于保存所有状态,但实际上这里我们只需要一维数组维护前一个状态就可以推导出当前结果

int rucksackDp2(int *weight, int num, int w)
{//先定义数组int *status = (int *)calloc( num, sizeof(int )  );//初始化第一行的值status[0]= 1; //不放if ( weight[0] < w){status[weight[0]] = 1; //放}//从第二个开始考虑for (int i = 1; i < num; i++){//不放: 保持状态不变//放: 在上一个状态基础上, 将增加后的重量对应位置设置为1for ( int j = 0; j <= w -weight[i]; j++){if ( status[j] == 1) status[j+weight[i]] = 1;}}//输出结果for ( int i = w; i >= 0; --i){if (status[i] == 1) return i;}return 0;
}

写动态规划代码的关键在于状态定义和状态转移方程。在0-1背包问题中,我们定义的状态是status[i]就是当前决策结束后到达的重量,而转移方程就是if ( status[j] == 1) status[j+weight[i]] = 1;

这篇关于如何用动态规划解决0-1背包问题(C语言实现)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1088150

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

2024.6.24 IDEA中文乱码问题(服务器 控制台 TOMcat)实测已解决

1.问题产生原因: 1.文件编码不一致:如果文件的编码方式与IDEA设置的编码方式不一致,就会产生乱码。确保文件和IDEA使用相同的编码,通常是UTF-8。2.IDEA设置问题:检查IDEA的全局编码设置和项目编码设置是否正确。3.终端或控制台编码问题:如果你在终端或控制台看到乱码,可能是终端的编码设置问题。确保终端使用的是支持你的文件的编码方式。 2.解决方案: 1.File -> S

vcpkg安装opencv中的特殊问题记录(无法找到opencv_corexd.dll)

我是按照网上的vcpkg安装opencv方法进行的(比如这篇:从0开始在visual studio上安装opencv(超详细,针对小白)),但是中间出现了一些别人没有遇到的问题,虽然原因没有找到,但是本人给出一些暂时的解决办法: 问题1: 我在安装库命令行使用的是 .\vcpkg.exe install opencv 我的电脑是x64,vcpkg在这条命令后默认下载的也是opencv2:x6

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

问题-windows-VPN不正确关闭导致网页打不开

为什么会发生这类事情呢? 主要原因是关机之前vpn没有关掉导致的。 至于为什么没关掉vpn会导致网页打不开,我猜测是因为vpn建立的链接没被更改。 正确关掉vpn的时候,会把ip链接断掉,如果你不正确关掉,ip链接没有断掉,此时你vpn又是没启动的,没有域名解析,所以就打不开网站。 你可以在打不开网页的时候,把vpn打开,你会发现网络又可以登录了。 方法一 注意:方法一虽然方便,但是可能会有

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为