01背包——回溯

0x1 题目叙述

有n件物品和一个容量为c的背包。第i件物品的价值是v[i],重量是w[i]。求解将哪些物品装入背包可使价值总和最大。所谓01背包,表示每一个物品只有一个,要么装入,要么不装入。
输入:
第1行是背包容量
第2行物品个数n
第3行:n个物品的重量
第4行:n个物品的价值
输出:
第1行:最优值
第2行:最优解
输入输出范例:
输入:
50 //背包容量
4 //物品个数
30 20 40 10 //物品重量
10 20 30 40 //物品价值
输出:
70 //最优值
0 0 1 1 //最优解

该问题的约束函数是什么?

约束函数为:
∑wixi≤C,即背包能装下物品

0x2 代码

语言:C++
注意:下面的代码使用double型变量

考虑约束函数,不考虑限界函数
#include <iostream>
#include <stdio.h>

using namespace std;
int n;//物品数量
double c;//背包容量
double v[100];//各个物品的价值 value
double w[100];//各个物品的重量 weight
double cw = 0.0;//当前背包重量 current weight
double cp = 0.0;//当前背包中物品总价值 current value
double bestp = 0.0;//当前最优价值best price
double perp[100];//单位物品价值(排序后) per price
int order[100];//物品编号
int put[100];//设置是否装入,为1的时候表示选择该组数据装入,为0的表示不选择该组数据
int bestput[100];//最优解 0 1

//回溯函数
void backtrack(int i)
{   //i用来指示到达的层数(第几步,从0开始),同时也指示当前选择完了几个物品
    double bound(int i);
    if(i>n) //递归结束的判定条件
    {
        if(cp>bestp)
        {
            bestp = cp;
            for(int i=1;i<=n;i++)
            {
                bestput[i]= put[i];
            }
        }
        return;
    }
    //如若左子节点可行,则直接搜索左子树;

    if(cw+w[i]<=c)//将物品i放入背包,搜索左子树
    {
        cw+=w[i];//同步更新当前背包的重量
        cp+=v[i];//同步更新当前背包的总价值
        put[i]=1;
        backtrack(i+1);//深度搜索进入下一层
        cw-=w[i];//回溯复原
        cp-=v[i];//回溯复原
    }
    //右子树
    put[i]=0;
    backtrack(i+1);
}

int main()
{
    int i;
    for(i=0;i<100;i++)
        put[i]=0;
    printf("请输入物品的数量和背包的容量:");
    scanf("%d %lf",&n,&c);
    printf("请依次输入%d个物品的重量:\n",n);
    for(i=1;i<=n;i++){
        scanf("%lf",&w[i]);
        order[i]=i;
    }

    printf("请依次输入%d个物品的价值:\n",n);
    for(i=1;i<=n;i++){
        scanf("%lf",&v[i]);
    }

    backtrack(1);

    printf("最优价值为:%lf\n",bestp);
    printf("需要装入的物品编号是:");
    for(i=1;i<=n;i++)
    {
        if(bestput[i]==1)
            printf("%d ",order[i]);
    }
    return 0;
}
/*
test data
4 50
30 20 40 10
10 20 30 40
*/

考虑约束函数,考虑限界函数
#include <iostream>
#include <stdio.h>

using namespace std;
int n;//物品数量
double c;//背包容量
double v[100];//各个物品的价值 value
double w[100];//各个物品的重量 weight
double cw = 0.0;//当前背包重量 current weight
double cp = 0.0;//当前背包中物品总价值 current value
double bestp = 0.0;//当前最优价值best price
double perp[100];//单位物品价值(排序后) per price
int order[100];//物品编号
int put[100];//设置是否装入,为1的时候表示选择该组数据装入,为0的表示不选择该组数据
int bestput[100];//最优解 0 1

//回溯函数
void backtrack(int i)
{   //i用来指示到达的层数(第几步,从0开始),同时也指示当前选择完了几个物品
    double bound(int i);
    if(i>n) //递归结束的判定条件
    {
        if(cp>bestp)
        {
            bestp = cp;
            for(int i=1;i<=n;i++)
            {
                bestput[i]= put[i];
            }
        }
        return;
    }
    //如若左子节点可行,则直接搜索左子树;
    //对于右子树,先计算上界函数,以判断是否将其减去
    if(cw+w[i]<=c)//将物品i放入背包,搜索左子树
    {
        cw+=w[i];//同步更新当前背包的重量
        cp+=v[i];//同步更新当前背包的总价值
        put[i]=1;
        backtrack(i+1);//深度搜索进入下一层
        cw-=w[i];//回溯复原
        cp-=v[i];//回溯复原
    }
    if(bound(i+1)>bestp)//如若符合条件则搜索右子树
        {
            put[i]=0;
            backtrack(i+1);
        }
}

//计算上界函数,功能为剪枝
double bound(int i)
{   //判断当前背包的总价值cp+剩余容量可容纳的最大价值<=当前最优价值
    double leftw= c-cw;//剩余背包容量
    double b = cp;//记录当前背包的总价值cp,最后求上界
    //以物品单位重量价值递减次序装入物品
    while(i<=n && w[i]<=leftw)
    {
        leftw-=w[i];
        b+=v[i];
        i++;
    }
    //装满背包
    if(i<=n)
        b+=v[i]/w[i]*leftw;
    return b;//返回计算出的上界

}



int main()
{
    int i;
    for(i=0;i<100;i++)
        put[i]=0;
    printf("请输入物品的数量和背包的容量:");
    scanf("%d %lf",&n,&c);
    printf("请依次输入%d个物品的重量:\n",n);
    for(i=1;i<=n;i++){
        scanf("%lf",&w[i]);
        order[i]=i;
    }

    printf("请依次输入%d个物品的价值:\n",n);
    for(i=1;i<=n;i++){
        scanf("%lf",&v[i]);
    }

    backtrack(1);

    printf("最优价值为:%lf\n",bestp);
    printf("需要装入的物品编号是:");
    for(i=1;i<=n;i++)
    {
        if(put[i]==1)
            printf("%d ",order[i]);
    }
    return 0;
}
/*
test data
4 50
30 20 40 10
10 20 30 40
*/

考虑约束函数,考虑限界函数,排序
#include <iostream>
#include <stdio.h>

using namespace std;
int n;//物品数量
double c;//背包容量
double v[100];//各个物品的价值 value
double w[100];//各个物品的重量 weight
double cw = 0.0;//当前背包重量 current weight
double cp = 0.0;//当前背包中物品总价值 current value
double bestp = 0.0;//当前最优价值best price
double perp[100];//单位物品价值(排序后) per price
int order[100];//物品编号
int put[100];//设置是否装入,为1的时候表示选择该组数据装入,为0的表示不选择该组数据
int bestput[100];//最优解 0 1


//按单位价值排序
void knapsack()
{
    int i,j;
    int temporder = 0;
    double temp = 0.0;

    for(i=1;i<=n;i++)
        perp[i]=v[i]/w[i]; //计算单位价值(单位重量的物品价值)
    for(i=1;i<=n-1;i++)
    {
        for(j=i+1;j<=n;j++)
            if(perp[i]<perp[j])//冒泡排序perp[],order[],sortv[],sortw[]
        {
            temp = perp[i];  //冒泡对perp[]排序
            perp[i]=perp[i];
            perp[j]=temp;

            temporder=order[i];//冒泡对order[]排序
            order[i]=order[j];
            order[j]=temporder;

            temp = v[i];//冒泡对v[]排序
            v[i]=v[j];
            v[j]=temp;

            temp=w[i];//冒泡对w[]排序
            w[i]=w[j];
            w[j]=temp;
        }
    }
}

//回溯函数
void backtrack(int i)
{   //i用来指示到达的层数(第几步,从0开始),同时也指示当前选择完了几个物品
    double bound(int i);
    if(i>n) //递归结束的判定条件
    {
        if(cp>bestp)
        {
            bestp = cp;
            for(int i=1;i<=n;i++)
            {
                bestput[i]= put[i];
            }
        }
        return;
    }
    //如若左子节点可行,则直接搜索左子树;

    if(cw+w[i]<=c)//将物品i放入背包,搜索左子树
    {
        cw+=w[i];//同步更新当前背包的重量
        cp+=v[i];//同步更新当前背包的总价值
        put[i]=1;
        backtrack(i+1);//深度搜索进入下一层
        cw-=w[i];//回溯复原
        cp-=v[i];//回溯复原
    }
    if(bound(i+1)>bestp)//如若符合条件则搜索右子树
    {
        put[i]=0;
        backtrack(i+1);
    }
}

//计算上界函数,功能为剪枝
double bound(int i)
{   //判断当前背包的总价值cp+剩余容量可容纳的最大价值<=当前最优价值
    double leftw= c-cw;//剩余背包容量
    double b = cp;//记录当前背包的总价值cp,最后求上界
    //以物品单位重量价值递减次序装入物品
    while(i<=n && w[i]<=leftw)
    {
        leftw-=w[i];
        b+=v[i];
        i++;
    }
    //装满背包
    if(i<=n)
        b+=v[i]/w[i]*leftw;
    return b;//返回计算出的上界
}


int main()
{
    int i;
    for(i=0;i<100;i++)
        put[i]=0;
    printf("请输入物品的数量和背包的容量:");
    scanf("%d %lf",&n,&c);
    printf("请依次输入%d个物品的重量:\n",n);
    for(i=1;i<=n;i++){
        scanf("%lf",&w[i]);
        order[i]=i;
    }

    printf("请依次输入%d个物品的价值:\n",n);
    for(i=1;i<=n;i++){
        scanf("%lf",&v[i]);
    }

    knapsack();
    backtrack(1);

    printf("最优价值为:%lf\n",bestp);
    printf("需要装入的物品编号是:");
    for(i=1;i<=n;i++)
    {
        if(bestput[i]==1)
            printf("%d ",order[i]);
    }
    return 0;
}
/*
test data
4 50
30 20 40 10
10 20 30 40
*/

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值