• 栈: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
    2. #include
    3. #include
    4. #include
    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. }

  • 相关阅读:
    axios使用
    Android handlerThread并发了解
    JAVA中简单的for循环竟有这么多坑,你踩过吗
    Visual Studio部署C++矩阵库Armadillo的方法
    二叉树的锯齿形层序遍历[分层遍历方式之一 -> 前序遍历+level]
    PHP Curl请求封装
    MSDC 4.3 接口规范(5)
    ESP8266-Arduino编程实例-ML8511紫外线(UV)传感器驱动
    Redis数据结构:散列
    「SDOI2016」征途 题解
  • 原文地址:https://blog.csdn.net/qq_62792553/article/details/127598124