动态规划之背包问题(C语言)

动态规划

动态规划(英语:Dynamic programming,简称DP)是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划常常适用于有重叠子问题和最优子结构性质的问题
动态规划思想大致上为:若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。
由于通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。

背包问题

背包问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,如何选择才能使得物品的总价格最高。
背包问题是典型的动态规划问题。

而背包问题还存在需要恰好装满背包和不需要恰好装满两种情况
这篇文章所探讨的所有背包问题都是建立在不需要恰好装满的情况下

由简单背包问题的基础上又衍生出许多问题都可以采用动态规划解决。
例如:
1. 01背包问题(每种物品只有一件,放或者不放)
2. 完全背包问题(每件物品有无限件可用)
3. 多重背包问题(每件物品有n[i]件可用)


01背包问题

题目:有N件物品和一个容量为V的背包。第i件物品的费用是weight[i],价值是value[i]。求将哪些物品装入背包可使价值总和最大。

01背包问题是最基础的背包问题,”01”的意思是:每种物品仅有一件,放为“1”,不放为“0”。
我们假定f[i][v]为将前i件物品前恰好放入一个容量为V的背包中可获得的最大价值
则其状态转移方程是:

f[i][V]=max{f[i-1][V],f[i-1][V-weight[i]]+value[i]}

状态转移方程分析
由上图可知,背包问题可以简化为“将前i件物品放入容量为V的背包中”的问题,而这个问题可以优化为,“不放第i件物品“和“放第i件物品“的问题。

如果不放第i件物品,问题为将前i-1件物品放入容量为V的背包中,总价值为f[i-1][v]

如果放第i件物品,问题为前i-1件物品放入剩下的容量为V-weight[i]的背包中,价值为f[i-1][V-weight[i]]+value[i]
代码部分较为简单,主要学习动态规划这种思想:

    for (int i=1; i<=N; i++)  for (int j=1; j<=M; j++)  {  if (weight[i]<=j){  f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);  }  else  f[i][j]=f[i-1][j];  }  

该算法的时间复杂度一定降到最低,但是我们还可以通过压缩空间复杂度的方式优化代码,方法就是将二维数组f[i][j]转化为一维数组[j]只需将完整程序稍作修改即可,这里只给出核心代码。

    for(int i = 1; i <= N; ++i)for(int j = V; j >= weight[i]; --j)f[j] = max(f[j], f[j - weight[i]] + value[i]);  

具体代码如下:

#include<stdio.h>   
#define  V 1500  
int f[10][V];//全局变量,自动初始化为0  
int weight[10];  
int value[10];  
#define  max(x,y)   (x)>(y)?(x):(y)  
int main()  
{  //状态转移方程 f[i+1][j]=max{f[i][j],f[i][j-weight[i+1]+value[i+1]}int N,M; freopen("1.txt","r",stdin); scanf("%d%d",&N,&M);//N物品个数 M背包容量  for (int i=1;i<=N; i++)  {  scanf("%d%d",&weight[i],&value[i]); }  //动态规划for (int i=1; i<=N; i++)  for (int j=1; j<=M; j++)  {  if (weight[i]<=j)  {  f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);  }  else  f[i][j]=f[i-1][j];  }  printf("%d\n",f[N][M]);//输出最优解}

到这里已经基本实现01背包问题,但是该程序在输出的时候,只能输出最后的价值,不能知道选择的物品是哪个。在这里我们定义一个数组x[i],对于每一个物品,如果被选择置为“1”,否则为“0”。通过这样的方式来实现。
修改之后的代码如下:

#include<stdio.h>   
#define  V 100  
int f[10][V];//全局变量,自动初始化为0  
int weight[10];  
int value[10];  
#define  max(x,y)   (x)>(y)?(x):(y)  
int main()  
{  //初始化int N,M; freopen("1.txt","r",stdin); scanf("%d%d",&N,&M);//N物品个数 M背包容量  for (int i=1;i<=N; i++)  {  scanf("%d%d",&weight[i],&value[i]); }  //动态规划分析for (int i=1; i<=N; i++)  for (int j=1; j<=M; j++)  {  if (weight[i]<=j)  {  f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);  }  else  f[i][j]=f[i-1][j];  }  printf("%d\n",f[N][M]);//输出最优解//输出选择的物品int j = M;int x[V];for(int i=N; i>0; --i){if(f[i][j] > f[i-1][j]){x[i-1] = 1;j = j - weight[i-1];//装入第i-1个宝石后背包能装入的体积就只剩下j - V[i-1]}}for(int i=0; i<N; ++i)  printf("%d ", x[i]);  }   

完全背包问题

题目:有N件物品和一个容量为V的背包。第i件物品的费用是weight[i],价值是value[i]。每件物品可以无限选用,求将哪些物品装入背包可使价值总和最大。
完全背包问题不设定物品取用上限

对于算法的优化我们可以这样想:
在01背包问题中,我们要保证第i次循环中的f[i][v]是由f[i-1][V-weight[i]]递推而来,每一次都是“加选出一个(即一种)物品”而这种方式同时也保证了每件物品只选一次。
而完全背包问题的特点刚好是每种物品可选无限件,所以在考虑“加选出一个(即一种)物品”时就是单纯的考虑“加选出一个(可能为同一种)物品”,这样我们就需要考虑选入的物品是已经选入的情况。相比来说,反而简化了代码。

同样,我们假定f[i][v]为将前i件物品前恰好放入一个容量为V的背包中可获得的最大价值
则其状态转移方程是:

f[i][V]=max{f[i-1][V],f[i-1][V-k*weight[i]]+k*value[i]}

0<=k*weight[i]<=v,其中0<=k<=V/weight[i+1]

该状态方程等价于f[i][v]=max{f[i-1][v],f[i-1][V-weight[i]]+value[i]}
代码部分如下:

    for(int i = 1; i <= N; ++i)for(int j = 0; j <= M; ++j)for(int k = 0; k * weight[i] <= j; ++k)f[i][j] = max(f[i][j], f[i - 1][j - k * weight[i]] + k * value[i]);

一维数组优化:

    for(int i = 1; i <= N; ++i)for(int j = weight[i]; j <= V; ++j)f[j] = max(f[j], f[j - weight[i]] + value[i]); 

具体代码如下:

#include<stdio.h>    
#define  V 1500  
int f[10][V];//全局变量,自动初始化为0  
int weight[10];  
int value[10];  
#define  max(x,y)   (x)>(y)?(x):(y)  
int main()  
{  //f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v},其中0<=k<=V/weight[i+1]//f[j]=max(f[j],f[j-weight[i]]+value[i])    int N,M;  //初始化freopen("1.txt","r",stdin); scanf("%d%d",&N,&M);//N物品个数 M背包容量  for (int i=1;i<=N; i++)  {  scanf("%d%d",&weight[i],&value[i]); }  //动态规划for(int i = 1; i <= N; ++i)for(int j = 0; j <= M; ++j)for(int k = 0; k * weight[i] <= j; ++k)f[i][j] = max(f[i][j], f[i - 1][j - k * weight[i]] + k * value[i]);printf("%d",f[N][M]);//输出最优解  }

多重背包问题

题目:有N件物品和一个容量为V的背包。第i件物品最多有n[i]个,每个的费用是weight[i],价值是value[i]。每件物品最多可以选用相应的最大个数,求将哪些物品装入背包可使价值总和最大。
多重背包问题设定物品选择上限
代码部分如下:

    for(int i = 1; i <= N; ++i)for(int j = 0; j <= V; ++j)for(int k = 0; k <= num[i] && k * weight[i] <= j; ++k)f[i][j] = max(f[i-1][j], f[i - 1][j - k * weight[i]] + k * value[i]);

一维数组优化:

    for(int i = 1; i <= N; ++i)for(int j = V; j >= 0; --j)for(int k = 1; k <= num[i] && k * weight[i] <= j; ++k)f[j] = max(f[j], f[j - k * weight[i]] + k * value[i]);  

具体代码如下:

#include<stdio.h>    
#define  V 1500  
int f[10][V];//全局变量,自动初始化为0  
int weight[10];  
int value[10];   
int num[10];
#define  max(x,y)   (x)>(y)?(x):(y)  
int main()  
{  //f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v},其中0<=k<=V/weight[i+1]//f[j]=max(f[j],f[j-weight[i]]+value[i])    int N,M,cur;  freopen("2.txt","r",stdin); scanf("%d%d",&N,&M);//N物品个数 M背包容量  for (int i=1;i<=N; i++)  {  scanf("%d%d%d",&weight[i],&value[i],&num[i]); }  for(int i = 1; i <= N; ++i)for(int j = 0; j <= V; ++j)for(int k = 0; k <= num[i] && k * weight[i] <= j; ++k)f[i][j] = max(f[i-1][j], f[i - 1][j - k * weight[i]] + k * value[i]);printf("%d\n",f[N][M]);//输出最优解
}

注:由于测试输入数据较多,程序可以采用文件输入
5 10
2 6
2 3
6 5
5 4
4 6
多重背包需要输入每件物品的可选用次数,所以它的输入文件有所不同
5 10
2 6 1
2 3 1
6 5 1
5 4 1
4 6 1
之所以将次数都定为1,是说明01背包问题是多重背包的一种特殊情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/569875.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PHP函数-判断字符是否在于指定的字符串中

php中判断一个字符是否在字符串中 1、函数 以下四个函数都可以用来判断字符串中是否存在待查询的字符&#xff0c;可以是数字、字母或者符号。 strpos() - 查找字符串在另一字符串中第一次出现的位置&#xff08;区分大小写&#xff09; stripos() - 查找字符串在另一字符…

最大子列和问题(JAVA)

最大子列和 问题描述&#xff1a;给定N个整数的序列{A1&#xff0c;A2&#xff0c;A3&#xff0c;…&#xff0c;An}&#xff0c;求解子列和中最大的值。 这里我们给出{-2&#xff0c;11&#xff0c;-4&#xff0c;13&#xff0c;-5&#xff0c;-2}这样一个序列&#xff0c;正…

Oracle 11g必须开启的服务及服务详细介绍

成功安装Oracle 11g数据库后&#xff0c;你会发现自己电脑运行速度会变慢&#xff0c;配置较低的电脑甚至出现非常卡的状况&#xff0c;通过禁止非必须开启的Oracle服务可以提升电脑的运行速度。那么&#xff0c;具体该怎么做呢? 按照win7 64位环境下Oracle 11g R2安装详解中…

PHP—str_replace()替换函数的使用

一、str_replace()函数 1、定义和用法 str_replace() 函数替换字符串中的一些字符&#xff08;区分大小写&#xff09;。   注释&#xff1a;该函数是区分大小写的。请使用 str_ireplace() 函数执行不区分大小写的搜索。   注释&#xff1a;该函数是二进制安全的。 2、语…

JS/jQuery获取input的值和清空input的value值

一、获取input的值 1、通过普通选择器获取   通过类选择器获取&#xff1a;$(.class).val()   通过id选择器获取&#xff1a;$(" #id").val() 2、通过标签选择器获取   $(" input[ namename ] ").val()   $(" input[ typetext ] ").val…

JAVA刷题方法整理

JAVA刷题方法整理 一、String—>String[] 利用String.split()实现 注&#xff1a;在使用String.split 方法分隔字符串时&#xff0c;分隔符如果用到一些特殊字符&#xff0c;可能会得不到我们预期的结果&#xff0c;例如“|”&#xff0c;“*”&#xff0c;“”等&#x…

实现多线程Callable接口

Callable接口&#xff0c;实现多线程 1.实现 Callable接口&#xff0c;相较于实现 Runnable 接口的方式&#xff0c;优点是&#xff1a;方法可以有返回值&#xff0c;并且可以抛出异常 2.需要 FutureTask实现类的支持&#xff0c;用于接收运算结果 3.result.get()&#xff0c;接…

密码验证

用户在创建一个账户时&#xff0c;需要设置一个密码。密码的复杂程度是安全的保障之一&#xff0c;但是有些用户在设置密码时&#xff0c;总是把密码设置的过于简单&#xff0c;导致用户账户的安全存在威胁。因此&#xff0c;为了提高用户账户的安全性&#xff0c;添加了一个Ja…

阿里云windows/Linux 服务器建站教程,附WordPress配置方法

声明&#xff1a;文章仅供学习使用&#xff0c;故采用了多处链接&#xff0c;如有侵权&#xff0c;请私信我&#xff0c;立删。 最近看到一个学长做的验证界面&#xff0c;很简单的两个文本输入框&#xff0c;但是能给我们群里的小伙伴们做题提供一个验证答案的途径&#xff0…

Maven修改默认仓库为阿里云仓库

Maven 仓库默认在国外&#xff0c; 国内使用难免很慢&#xff0c;我们可以更换为阿里云的仓库。 第一步:修改 maven 根目录下的 conf 文件夹中的 setting.xml 文件&#xff0c;在 mirrors 节点上&#xff0c;添加内容如下&#xff1a; <mirrors><mirror><id>…

PHP语言结构详解

PHP语言结构 &#xff11;、语言结构释义   语言结构就是PHP语言的关键词&#xff0c;语言语法的一部分&#xff1b;   它不可以被用户定义或者添加到语言扩展或者库中&#xff1b;   它可以有也可以没有变量和返回值。 &#xff12;、为什么语言结构执行速度比函数快&…

查找算法——折半查找(JAVA)

折半查找 问题描述&#xff1a;给定一个整数X和整数A0&#xff0c;A1&#xff0c;A2……A(n-1)&#xff0c;后者已经预先排序并在内存中&#xff0c;求下标i使得Ai X &#xff0c;如果X不在数据中&#xff0c;则返回i -1。 我们首先可以想到的一种方法就是从左到右遍历&…

如何正确的检测对象类型?

在 javascript 中&#xff0c;我们常常用 typeof 运算符来检测对象的类型&#xff0c;在使用 typeof 检测引用类型的存储值会出现一个问题&#xff0c;无论引用的是什么类型的对象&#xff0c;它都会返回 "object"。这个时候我们往往会使用 instanceof instanceof 可…

PHP基于phpqrcode类生成二维码

使用ThinkPHP框架引入phpqrcode类生成二维码后&#xff0c;发现每次必须通过TP框架生成&#xff0c;略显繁琐&#xff0c;打算写一个简单的方法&#xff0c;然后运行php后直接批量生成二维码。方法也简单&#xff0c;直接写个PHP脚本&#xff0c;然后引入phpqrcode类&#xff0…

求最大公约数——欧几里得算法(JAVA)

欧几里得算法 问题描述&#xff1a;给出两个数m&#xff0c;n&#xff0c;求解这两个数的最大公因数 由于算法比较简单&#xff0c;这里不再赘述&#xff0c;我做的这个算法是默认了m>n,如果是对于任意两个数来说的话&#xff0c;我们这里还需要一个比较大小。 public cla…

编写函数digit(num, k),函数功能是:求整数num从右边开始的第k位数字的值,如果num位数不足k位则返回0。...

function digit(num,k){var knum 0;for(var i1; i<k; i){knum num%10;num parseInt(num/10);}return knum;}转载于:https://www.cnblogs.com/tis100204/p/10310140.html

JS/jQuery添加和移除CSS样式

有时候需要添加CSS样式和移除CSS样式&#xff0c;如添加display属性&#xff0c;设为隐藏。有时候需要移除display属性。 一、jQuery移除CSS样式的两种方法 注意&#xff1a;当其中一种不支持时&#xff0c;就尝试另一种&#xff1a; $("#show").removeAttr("…

高效幂运算(JAVA)--拆分解法、二进制解法

高效幂运算 问题描述&#xff1a;假设有一机器能够存储这样一些大整数&#xff08;或有一个编译程序能够模拟它&#xff09;&#xff0c;求一个相对大的数字&#xff08;一般为400位左右&#xff09;的极大幂&#xff08;400位左右&#xff09; 显然我们可以使用Java内置函数p…

动态规划之背包问题(JAVA)

背包问题之前的C语言版本已经将思路解析的差不多&#xff0c;虽然还有些许错误需要改正&#xff0c;但大体思路是正确的&#xff0c;需要的读者请参阅动态规划之背包问题&#xff08;C语言&#xff09; 背包问题本身就是典型的动态规划问题&#xff0c;所以这里只给出动态规划…

PHP常量:define和const的不同之处

&#xff11;、定义   常量是一个简单的标识符。在脚本执行期间该值不能改变&#xff08;除了所谓的魔术常量&#xff0c;他们其实不是常量&#xff09;。常量默认大小写敏感。通常常量标识符总是大写的。   可以用define()函数来定义常量。在php5.3.0以后&#xff0c;可以…