动态规划

动态规划(英语: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背包问题是多重背包的一种特殊情况。

动态规划之背包问题(C语言)相关推荐

  1. c语言背包问题装字母,C语言动态规划之背包问题详解

    01背包问题 给定n种物品,和一个容量为C的背包,物品i的重量是w[i],其价值为v[i].问如何选择装入背包的物品,使得装入背包中的总价值最大?(面对每个武平,只能有选择拿取或者不拿两种选择,不能选 ...

  2. 动态规划01背包问题入门学习,详细笔记,推荐阅读

    问题描述: 给定N种物品和一个背包.物品i的重量是Wi,其价值位Vi ,背包的容量为C.问应该如何选择装入背包的物品,使得转入背包的物品的总价值为最大?? 在选择物品的时候,对每种物品i只有两种选择, ...

  3. 算法:动态规划窃贼问题C语言实现

    算法:动态规划窃贼问题C语言实现 目录 算法:动态规划窃贼问题C语言实现 第一章 问题描述 1.1问题描述 第二章 算法思想及算法设计分析 2.1算法思想 2.2设计算法 2.3算法分析 2.4填表结 ...

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

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

  5. Suzy心情很差因为被charge了late fee Day42 | 动态规划之背包问题,416. 分割等和子集

    背包问题 01背包 Given n种物品,每种物品只有1个 每个物品有自己的重量.价值. 背包最大能装载的重量 动规五部曲--用二维数组 定义dp数组的含义:dp[ i ][ j ]表示[0,i]物品 ...

  6. 【算法与数据结构】—— 动态规划之背包问题

    动态规划之背包问题 前面介绍了一些最常见的动态规划题型和对应解法,而实际上,动态规划最经典的题型非背包问题莫属,并且大多数人最初都是从背包问题入坑进而打开动态规划这一大门. 背包问题分为多种,其中最常 ...

  7. 动态规划之背包问题的一些基础简单入门题

    前言 参考视频教程洛谷试练场 普及组 动态规划的背包问题 主要有01背包问题.完全背包问题.分组背包问题. 01背包问题一般从右往左推: 完全背包问题一般从左往右推: 分组背包一般用01的方法但需要记 ...

  8. 简单最短路径问题编程c语言,最短路径动态规划问题及C语言_实现.pdf

    第33卷 第2期 内 蒙 古 农 业 大 学 学 报 V01.33 No.2 2012年 3月 Mar.2012 Journalof Inner Mongolia AgriculturalUniver ...

  9. 动态规划之背包问题——01背包

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题--01背包 动态规划之背包问题--完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 ...

  10. 动态规划解决背包问题

    动态规划解决背包问题 问题描述: (1)解法一: 解决思路:动态规划 状态索引范围:从1开始 价值数组,大小数组索引范围:从0开始 状态:F(i,j):前i个物品放入大小为j的背包中所获得的最大价值. ...

最新文章

  1. python详细安装教程linux-在Linux上安装Python3
  2. COM原理与应用之COM的实现
  3. PHP二维数组根据字段排序
  4. 构造函数内部原理 包装类
  5. 从底层重学 Java 之两大浮点类型 GitChat链接
  6. 19. Python 类的重写   类的私有变量和私有方法
  7. HTML5的视频格式之争
  8. np.c_和np.r_
  9. python 图表_python可视化入图表|贵州茅台净利润分析
  10. opencv-4.5.5-vc14_vc15配置环境VC++
  11. 使用hue时hive的历史查询记录结果无法下载
  12. 触控笔和pencil笔一样吗?ipad可用的触控笔
  13. opencv-图像翻转问题
  14. 南邮 OJ 1408 火星探险
  15. [2018国家集训队][UOJ449] 喂鸽子 [dp+组合数学]
  16. D3D11 加载静态3D模型(.obj格式)
  17. Java基础面试题精选汇总,备战面试突破篇--直击offer
  18. git 初次使用的配置流程
  19. Centos安装Docker
  20. Photoshop 2023 Beta内置Ai创意填充绘图!

热门文章

  1. mysql timestamp排序_对多个表进行排序MYSQL TimeStamp
  2. python新奇检测_3种Python检测URL状态
  3. linux无缝升级的版本,最近一周发布新版本的Linux发行版 - 2020-01-03
  4. mysql身份验证失败_SMTP身份验证失败PAM-MySQL无法进行身份验证
  5. 后发送过慢的问题_点胶阀使用常见问题怎么解决
  6. Spring Boot JDBC
  7. R语言基础入门(4)之数据类型与相应运算2
  8. java线程池执行器_Java线程池ThreadPoolExecutor的使用
  9. python里面的tuple与list对比
  10. C++纯虚函数与抽象类