当前位置:   article > 正文

栈:01背包问题(带价值) 栈解法/回溯算法 递归算法【C语言,数据结构】(内含源代码)_背包问题递归算法

背包问题递归算法

目录

 题目:

需求分析:

概要设计:

详细设计:

递归解法:

栈解法(回溯算法):

总结:

用户手册:

测试结果:

 源代码:


 题目:

背包问题(Knappsack Problem).

分别设计背包问题的递归算法,和利用栈解决背包问题的非递归算法,分析比较两种算法的时间复杂度和空间复杂度。

需求分析:

1.用递归算法解决背包问题

2.用栈解决背包问题

3.输入两个数代表背包容量与物品个数。然后分两行输入物品重量和价值

例如:

  1. 1 2
  2. 1 2
  3. 2 1

表示,背包容量为1,有两个物品,物品重量是:1和2。物品价值是:2和1。

4.程序执行命令包括:(1)收集输入 (2)用递归计算 (3)用栈计算

5.测试数据:

  1. 8
  2. 5
  3. 4 5 2 1 3
  4. 3 5 1 2 2
  5. 答案:8
  6. 8
  7. 4
  8. 2 3 4 5
  9. 3 4 5 8
  10. 答案:12
  11. 10
  12. 5
  13. 5 1 3 1 2
  14. 4 2 3 5 2
  15. 答案:14

概要设计

1.输入数据用两个数组存储。

2.主程序模块:

void main(){

        接收输入;

        递归处理;

        栈处理;

        输出;

}

详细设计:

用递归和栈的思想都一样,那就是要试过商品的所有排列组合,找到价值最大的一组

大概思路就是,模拟用背包去装物品的过程,一个一个遍历物品,如果装得下就装,装不下就跳过这个物品看下一个,装满了就判断,这一种排列组合的商品价值如果大于上一组,就改变记录的最大价值,然后把包里最后一个物品拿出来,选择不装他,接着往下遍历。

递归解法:

递归的核心思想与上述描述也没有太大区别,递归使用一个数组,来保存截止上一个物品,前面的物品能装多大价值,通过前面的结果,计算出第i个物品的结果。

用一个i来表示,截止计算到第i件物品,这个背包对前i件物品的最佳选择,然后传入下一个递归r(i+1)。

最佳选择结果怎么来的?这个也不是什么智能选择,通过比较,背包里装第i件物品,和不装第i件物品,选出价值最大的,就是最佳选择。

为什么不装第i件物品会更好?因为背包空间是有限的,这里指的装意思是,为了一定装下这件物品,我要找到前面背包空间刚好能装下第i件物品的状态,然后把这件物品装进去。不装的意思是,保留前一次最佳的状态,不装该物品。

  1. int GetMaxVR(int sum, int nowWeight, int step, int *w, int *v, int n, int bag) {
  2. //背包,递归
  3. int max = 0;
  4. int maxb = 0;
  5. if(nowWeight > bag) //超出重量 则回退
  6. return max;
  7. if(step == n) { //已经完成n件物品的选择
  8. if(sum > max)
  9. max = sum; // 更新最大价值
  10. return max;
  11. }
  12. max = GetMaxVR(sum + v[step], nowWeight + w[step], step + 1, w, v, n, bag); //选这件物品
  13. maxb = GetMaxVR(sum, nowWeight, step + 1, w, v, n, bag); //不选这件物品
  14. if(max > maxb) { //结果返回
  15. return max;
  16. } else {
  17. return maxb;
  18. }
  19. }

如果看代码也不清楚可以看一下这篇博客:

http://t.csdn.cn/U960r

栈解法(回溯算法):

栈的解法就相对更简单了,栈就是我们的背包。

从一开始,把第i件物品放入栈里,如果背包还能装,则装下第i件物品,在判断下一件。

当背包满的时候,就计算背包里的物品价值,大于则记录新最大价值,然后最后一件出栈,把指针i指向弹出的最后一件的下一个物品,这里就代表不装弹出的那一件物品,继续上述操作。

当然还有特殊情况,就是到最后一件了,还没有装满栈。依然重复出栈操作,最后一件出栈,把指针i指向弹出的最后一件的下一个物品,继续向下重复入栈操作。

用图来掩饰就是这样的。

20a3dd8b4d234ca890e72492baae44d4.gif

  1. int GetMaxVS(int *w, int *v, int n, int bag) {
  2. //背包,栈,回溯
  3. int bagw = 0, bagv = 0, MValue = 0;
  4. SqStack bagi;
  5. InitStack(&bagi);
  6. for(int i = 0; StackEmpty(bagi) != OK || i < n; i++) {
  7. if(bagw == bag || i >= n) { //背包满或遍历完,则计算结果,并回溯
  8. if(bagv > MValue) {
  9. MValue = bagv;
  10. }
  11. Pop(&bagi, &i);
  12. bagw -= w[i];
  13. bagv -= v[i];
  14. } else if(bag >= bagw + w[i]) { //不满则继续遍历
  15. Push(&bagi, i);
  16. bagw += w[i];
  17. bagv += v[i];
  18. }
  19. }
  20. return MValue;
  21. }

总结:

递归方法还有一种思路,用f[n]个数组来存储选择到第i件物品时的最优价值,这样虽然空间复杂度大了,但更利于人来理解递归是怎么工作的。在这里,我们选用的是机器效率最高的一种递归。

用户手册:

1.本程序为DOS环境。

2.进入后,根据提示输入背包容量、物品个数、物品价值和重量后回车。

3.根据输入,程序会显示两种方法计算到的最大价值结果

(两种方法计算的最大价值应该相同)

测试结果:

41e386e225e94c3c9e090cbb87ffd7a7.png

 855fe77566b04488bc965e59a6d1fc35.png

 e403efc013164df1816f7891b3a05e9c.png

 源代码:

头文件:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <conio.h>
  4. #include <malloc.h>
  5. #define TRUE 1 //函数结果状态码
  6. #define FALSE 0
  7. #define ERROR 0
  8. #define OK 1
  9. #define EQUAL 1
  10. #define OVERFLOW -1
  11. #define INFEASIBLE -2
  12. #define STACK_INIT_SIZE 100 //存储空间初始分配量
  13. #define STACKINCREMENT 10 //存储空间分配增量
  14. typedef int Status; //Status 为函数返回值类型,其值为函数结果状态代码
  15. typedef int SElemType; //栈中数据元素类型为int型
  16. typedef struct { //栈的顺序存储表示
  17. SElemType *base; //栈构造之前和销毁之后,其值为NULL
  18. SElemType *top; //栈顶指针
  19. int stacksize; //当前已分配的存储空间
  20. } SqStack;
  21. Status InitStack(SqStack *); //栈初始化
  22. Status DestroyStack(SqStack *); //销毁栈
  23. Status StackEmpty(SqStack); //栈判空
  24. Status GetTop(SqStack, SElemType *); //取栈顶元素
  25. Status Push(SqStack *, SElemType); //入栈
  26. Status Pop(SqStack *, SElemType *); //出栈
  27. Status StackTraverse(SqStack); //遍历栈,输出每个数据元素
  28. Status InitStack(SqStack *S) {
  29. //构造一个空栈Ss
  30. S->base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
  31. if(!S->base) {
  32. //存储分配失败
  33. exit(OVERFLOW);
  34. }
  35. S->top = S->base;
  36. S->stacksize = STACK_INIT_SIZE;
  37. return OK;
  38. }//InitStack
  39. Status GetTop(SqStack S, SElemType *e) {
  40. //若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
  41. if(S.top == S.base) {
  42. return ERROR;
  43. }
  44. *e = *(S.top - 1);
  45. return OK;
  46. }//GetTop
  47. Status Push(SqStack *S, SElemType e) {
  48. //插入元素e为新的栈顶元素
  49. if(S->top - S->base >= S->stacksize) {
  50. //栈满,追加存储空间
  51. S->base = (SElemType *)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(SElemType));
  52. if(!S->base) {
  53. //存储分配失败
  54. exit(OVERFLOW);
  55. }
  56. S->top = S->base + S->stacksize;
  57. S->stacksize += STACKINCREMENT;
  58. }
  59. *S->top++ = e;
  60. return OK;
  61. }//Push
  62. Status Pop(SqStack *S, SElemType *e) {
  63. //若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROE
  64. if(S->top == S->base) {
  65. return ERROR;
  66. }
  67. *e = *(--S->top);
  68. return OK;
  69. }//Pop
  70. Status DestoryStack(SqStack *S) {
  71. //销毁栈S
  72. if(S->base) {
  73. free(S->base);
  74. }
  75. S->top = S->base = NULL;
  76. return OK;
  77. }//DestroyStack
  78. Status StackEmpty(SqStack S) {
  79. //栈判空
  80. if(S.base == S.top) {
  81. return OK;
  82. }
  83. return ERROR;
  84. }
  85. Status StackTraverse(SqStack S) {
  86. //遍历栈,输出每一个数据元素
  87. SElemType *p = S.base;
  88. int i = 0;
  89. if(S.base == S.top) {
  90. printf("Stack is Empty.\n");
  91. return OK;
  92. }
  93. while(p < S.top) {
  94. printf("[%d:%d]\n", ++i, *p++);
  95. }
  96. return OK;
  97. }//StackTraverse

主程序:

  1. #include "SqStack.h"
  2. Status input(int **, int **, int *, int *); //输入程序
  3. int GetMaxVR(int, int, int, int *, int *, int, int); //背包,递归
  4. int GetMaxVS(int *, int *, int, int); //背包,栈,回溯
  5. int main() {
  6. int *w, *v;
  7. int n, bag;
  8. int maxR, maxS;
  9. input(&w, &v, &n, &bag);
  10. maxR = GetMaxVR(0, 0, 0, w, v, n, bag);
  11. maxS = GetMaxVS(w, v, n, bag);
  12. printf("Max value is: %d(stack) and %d(recursion)", maxS, maxR);
  13. return 0;
  14. }//main
  15. Status input(int **w, int **v, int *n, int *bag) {
  16. //输入程序
  17. printf("How much can the backpack hold?\n");
  18. scanf("%d", bag);
  19. printf("How many objects will there be?\n");
  20. scanf("%d", n);
  21. *w = (int *)malloc(sizeof(int) * (*n));
  22. *v = (int *)malloc(sizeof(int) * (*n));
  23. printf("Enter weights in order:\n");
  24. for(int i = 0; i < *n; i++) {
  25. scanf("%d", *w + i);
  26. }
  27. printf("Enter value in order:\n");
  28. for(int i = 0; i < *n; i++) {
  29. scanf("%d", *v + i);
  30. }
  31. return OK;
  32. }
  33. int GetMaxVR(int sum, int nowWeight, int step, int *w, int *v, int n, int bag) {
  34. //背包,递归
  35. int max = 0;
  36. int maxb = 0;
  37. if(nowWeight > bag) //超出重量 则回退
  38. return max;
  39. if(step == n) { //已经完成n件物品的选择
  40. if(sum > max)
  41. max = sum; // 更新最大价值
  42. return max;
  43. }
  44. max = GetMaxVR(sum + v[step], nowWeight + w[step], step + 1, w, v, n, bag); //选这件物品
  45. maxb = GetMaxVR(sum, nowWeight, step + 1, w, v, n, bag); //不选这件物品
  46. if(max > maxb) { //结果返回
  47. return max;
  48. } else {
  49. return maxb;
  50. }
  51. }
  52. int GetMaxVS(int *w, int *v, int n, int bag) {
  53. //背包,栈,回溯
  54. int bagw = 0, bagv = 0, MValue = 0;
  55. SqStack bagi;
  56. InitStack(&bagi);
  57. for(int i = 0; StackEmpty(bagi) != OK || i < n; i++) {
  58. if(bagw == bag || i >= n) { //背包满或遍历完,则计算结果,并回溯
  59. if(bagv > MValue) {
  60. MValue = bagv;
  61. }
  62. Pop(&bagi, &i);
  63. bagw -= w[i];
  64. bagv -= v[i];
  65. } else if(bag >= bagw + w[i]) { //不满则继续遍历
  66. Push(&bagi, i);
  67. bagw += w[i];
  68. bagv += v[i];
  69. }
  70. }
  71. return MValue;
  72. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/332929
推荐阅读
相关标签
  

闽ICP备14008679号