基于堆栈的背包问题

问题描述:假设有一个能装入总体积为T的背包和n件体积分别为w_1,w_2,...w_n的物品,能否从n件物品中挑选若干件恰好装满背包,即使w_1+w_2+...+w_n=T,要找出所有满足上述条件的解。

测试数据:T=10,各件物品的体积为{1,8,4,3,5,2},可找到下列4组解:

(1,4,3,2)、(1,4,5)、(8,2)、(3,5,2)

基本要求:

(1)、设计一个背包问题的函数。

(2)、编写一个测试主函数。

测试数据:T=10,各件物品的体积为{1,8,4,3,5,2}。

算法思想:

        首先将物品排成一列,然后顺序选取物品装入背包中。假设选取了前i件物品之后还没有装满,则继续选取第i+1件物品;若该件物品“太大”不能装入,则弃之而继续选取下一件,直至背包装满为止;但如果在剩余的物品中找不到合适的物品以填满背包,则说明“刚刚”装入背包的那件物品“不合适”,应将它取出“弃之一边”,继续再从“它之后”的物品中选取;如此重复,直至求的满足条件的解,或者无解。由于回退重选规则就是“后进先出”规则,因此自然要使用堆栈。

数据结构:

/**堆栈的数据结构**/
typedef struct snode
{
    int weight;    /**定义权值**/
    int i;         /**定义在数组中的位置**/
    struct snode *next;
}LSnode;
/**背包的数据结构**/
typedef struct knapsack
{
    int Location;         /**定义数组内的位置**/
    int Choose_flag;      /**定义是否在堆栈标志位**/
    int Pop_flag;         /**定义是否出栈标志位**/
}pack;

 

首先我们先设计堆栈,分为堆栈初始化,判断堆栈是否为空函数,入栈,出栈,取栈顶元素,销毁堆栈几个操作函数,其代码如下:

/**stack.c**/
#include "stack.h"

/**堆栈初始化函数**/
void StackInitiate(LSnode **head)
{
    *head = (LSnode *)malloc(sizeof(LSnode));
    (*head)->next = NULL;
}
/**堆栈非空否函数**/
int StackNotEmpty(LSnode *head)
{
    if(head->next == NULL) return 0;
    return 1;
}
/**入栈函数**/
void StackPush(LSnode *head,int weight,int i)
{
    LSnode *PreNode;
    PreNode = (LSnode *)malloc(sizeof(LSnode));

    PreNode->next = head->next;
    PreNode->weight = weight;
    PreNode->i = i;
    head->next = PreNode;
}

/**出栈函数**/
int StackPop(LSnode *head,int *weight,int *i)
{
    LSnode *PreNode;
    PreNode = head->next;
    if(PreNode == NULL)
    {
        printf("Stack is empty!\n");
        return 0;
    }
    head->next = PreNode->next;
    *weight = PreNode->weight;
    *i = PreNode->i;
    free(PreNode);
    return 1;
}

/**取出栈顶元素函数**/
int StackTop(LSnode *head,int *weight,int *i)
{
    LSnode *PreNode;
    PreNode = head->next;
    if(PreNode == NULL)
    {
        printf("Stack is empty!\n");
        return 0;
    }
    *weight = PreNode->weight;
    *i = PreNode->i;
    return 1;
}
/**销毁栈顶元素**/
void Destroy(LSnode *head)
{
    LSnode *CurNode;
    LSnode *PreNode;

    CurNode = head;
    while(CurNode != NULL)
    {
        PreNode = CurNode;
        CurNode = CurNode->next;
        free(PreNode);
    }
}

相应的头文件如下:

/**stack.h**/
#ifndef _STACK_H
#define _STACK_H
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef struct snode
{
    int weight;    /**定义权值**/
    int i;         /**定义在数组中的位置**/
    struct snode *next;
}LSnode;
void StackInitiate(LSnode **head);
int StackNotEmpty(LSnode *head);
void StackPush(LSnode *head,int weight,int i);
int StackPop(LSnode *head,int *weight,int *i);
int StackTop(LSnode *head,int *weight,int *i);
void Destroy(LSnode *head);
#endif // _STACK_H

 背包设计函数分为,结构体初始化,信息查询函数,信息赋值函数,权值打印函数,找寻权值组合函数,相应的注释都标在代码中:

/**knapsack.c**/
#include "knapsack.h"
/**初始化函数**/
void PackInitiate(pack array[],int n)
{
    int i;
    for(i = 0;i < n;i++)
    {
        array[i].Location = -1;
        array[i].Choose_flag = 0;
        array[i].Pop_flag = 0;
    }
}
/**信息查询函数**/
void ServiceInformation(pack array[],int i,int n,int *Choose_flag,int *Pop_flag)
{
    int j;
    for(j=0;j<n;j++)
    {
        if(array[j].Location == i)
        {
            *Choose_flag = array[j].Choose_flag;
            *Pop_flag = array[j].Pop_flag;
             break;
        }
    }
    if(j == n)
    {
        *Choose_flag = 0;
        *Pop_flag = 0;
    }
}
/**信息赋值函数,将Pop_flag置为输入的值**/
void EndowedInformation(pack array[],int i,int n)
{
    int j;
    for(j=0;j<n;j++)
    {
        if(array[j].Location == i)
        {
            array[j].Pop_flag = 1;
            break;
        }
    }
}
/**得到权值函数**/
void GetWeight(int Weightarray[],int n,int *GoalWeight)
{
    int i;
    printf("请输入目标权重:\n");
    scanf("%d",GoalWeight);
    printf("请输入%d个数据:\n",n);
    for(i=0;i<n;i++)
    {
        scanf("%d",&Weightarray[i]);
    }
}
/**打印权值函数**/
void Printweight(LSnode *head)
{
    int weight,weight_i;
    printf("权值如下:\n");
    while(StackNotEmpty(head))
    {
        StackPop(head,&weight,&weight_i);
        printf("%d ",weight);
    }
    printf("\n");
}
/**找到相应权值组合函数**/
int FindWeightCombination(int Weightarray[],int *GoalWeight,int n)
{
    LSnode *head;
    int i = 0;
    int weight_i;
    int count = 0;
    int weight = 0;             /**定义出栈权值**/
    int GoalWeightTemp = 0;     /**定义目标权重寄存值**/
    int Choose_flag = 0;
    int Pop_flag = 0;
    int push_flag;
    pack array[n];          /**定义一个数组存储已在堆栈里面**/
    StackInitiate(&head);   /**初始化堆栈**/
    PackInitiate(array,n);  /**初始化结构体变量**/
    StackPush(head,Weightarray[i],i); /**将第0项压入结构体**/
    push_flag = 1;                  /**另push_flag = 1,防止第一个权值二次入栈**/
    while(StackNotEmpty(head))
    {
        ServiceInformation(array,i,n,&Choose_flag,&Pop_flag);
        if(!Choose_flag && !Pop_flag && i<n)  /**当前位置的值是否被访问过**/
            GoalWeightTemp = GoalWeightTemp + Weightarray[i];

        if(GoalWeightTemp == *GoalWeight)      /**当找到权值组合的时候我们结束循环**/
        {
            if(!push_flag)                       /**防止第一个权值二次入栈**/
                StackPush(head,Weightarray[i],i);

            printf("Finding Weight goal successfully!\n");
            break;
        }
        else if(GoalWeightTemp < *GoalWeight && i < n && !Choose_flag && !Pop_flag) /**当前权值小于目标权值的时候我们将当前元素入栈**/
        {
            if(!push_flag)
                StackPush(head,Weightarray[i],i);
            array[count].Location = i;
            array[count].Choose_flag = 1;
            count++;

        }
        else if(GoalWeightTemp > *GoalWeight && i < n && !Pop_flag && !Choose_flag)/**当前权值大于目标权值的时候我们减掉当前元素**/
        {
             GoalWeightTemp = GoalWeightTemp - Weightarray[i];
        }
        else if(i == n && GoalWeightTemp != *GoalWeight)        /**当一遍权值试过之后无法得到相应的权值,说明当前栈顶的元素不合适,出栈**/
        {
            StackPop(head,&weight,&weight_i);   /**出栈**/
            EndowedInformation(array,weight_i,n);   /**将Pop_flag置1**/
            GoalWeightTemp = GoalWeightTemp - weight;
            i = 0;
            continue;
        }
        if(i<n) /**当i<n时i++**/
          i++;
          push_flag = 0;
    }

    if(GoalWeightTemp != *GoalWeight)
    {
        printf("无法找到相应的组合!\n");
        return 0;
    }
    Printweight(head);
    Destroy(head);
    return 1;
}

相应的头文件如下:

/**knapsack.h**/
#ifndef _KNAPSACK_H
#define _KNAPSACK_H
#include "stack.h"
typedef struct knapsack
{
    int Location;         /**定义数组内的位置**/
    int Choose_flag;      /**定义是否在堆栈标志位**/
    int Pop_flag;         /**定义是否出栈标志位**/
}pack;

void PackInitiate(pack array[],int n);
void ServiceInformation(pack array[],int i,int n,int *Choose_flag,int *Pop_flag);
void EndowedInformation(pack array[],int i,int n);
void GetWeight(int Weightarray[],int n,int *GoalWeight);
void Printweight(LSnode *head);
int  FindWeightCombination(int Weightarray[],int *GoalWeight,int n);

#endif // _KNAPSACK_H

最终的主函数如下:

/**main.c**/
#include "stack.h"
#include "knapsack.h"
int main()
{
    int n;            /**定义个数**/
    int GoalWeight;   /**定义目标权值**/
    int i,j;
    int curtemp,pretemp;         /**定义缓存值**/
    printf("请输入物体个数:\n");
    scanf("%d",&n);
    if(n<0)
    {
        printf("输入错误!\n");
        exit(0);
    }
    int Weightarray[n];    /**定义权值数组**/
    GetWeight(Weightarray,n,&GoalWeight); /**输入相应信息**/
    for(i=0;i<n;i++)
    {
        printf("以权值%d为栈底时:\n",Weightarray[0]);
        FindWeightCombination(Weightarray,&GoalWeight,n);/**输出权值组合**/
        /**这里我们将每个输入的权值进行圆周循环,找到所有的权值集合**/
        for(j=0;j<n;j++)
        {
            if(j==0)
            {
                curtemp = Weightarray[j+1];
                Weightarray[j+1] = Weightarray[j];
            }
            else if(j<n-1)
            {
                pretemp = Weightarray[j+1];
                Weightarray[j+1] = curtemp;
                curtemp = pretemp;
            }
            else
            {
                Weightarray[0] = curtemp;
            }
        }
    }

    return 0;
}

最终运行结果如下图:

完。

问题描述: 假设有一个能装入总体积为T的背包和n件体积分别为w1 , w2 , … , wn 的物品,能否从n件物品中挑选若干件恰好装满背包,即使w1 +w2 + … + wn=T,要求找出所有满足上述条件的解。例如:当T=10,各件物品的体积{1,8,4,3,5,2}时,可找到下列4组解: (1,4,3,2) (1,4,5) (8,2) (3,5,2)。 问题提示: 可利用回溯法的设计思想来解决背包问题。首先将物品排成一列,然后顺序选取物品装入背包,假设已选取了前i 件物品之后背包还没有装满,则继续选取第i+1件物品,若该件物品"太大"不能装入,则弃之而继续选取下一件,直至背包装满为止。但如果在剩余的物品中找不到合适的物品以填满背包,则说明"刚刚"装入背包的那件物品"不合适",应将它取出"弃之一边",继续再从"它之后"的物品中选取,如此重复,直至求得满足条件的解,或者无解。 题目之二: 问题描述: 假设有n件物品,这些物品的重量分别是W1 , W2 , … , Wn,物品的价值分别是V1,V2, …,Vn。求从这n件物品中选取一部分物品的方案,使得所选中的物品的总重量不超过限定的重量W(W<∑Wi, i=1,2,┅,n),但所选中的物品价值之和为最大。 问题提示: 利用递归寻找物品的选择方案。假设前面已有了多种选择的方案,并保留了其中总价值最大的方案于数组option[]中,该方案的总价值保存于变量max_value中。当前正在考察新方案,其物品选择情况保存于数组eop[]中。假设当前方案已考虑了i-1件物品,现在要考虑第i件物品:当前方案已包含的物品的重量之和为tw;因此,若其余物品都选择是可能的话,本方案所能达到的总价值的期望值设为tv。引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值max_value时,继续考察当前方案已无意义,应终止当前方案而去考察下一个方案。 第i件物品的选择有两种可能: ① 物品i被选择。这种可能性仅当包含它不会超过方案总重量的限制才是可行的。选中之后继续递归去考虑其余物品的选择; ② 物品i不被选择。这种可能性仅当不包含物品i也有可能找到价值更大的方案的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值