zoukankan      html  css  js  c++  java
  • 回溯法解决0-1背包问题

    问题描述:

      有n件物品和一个容量为c的背包。第i件物品的价值是v[i],重量是w[i]。求解将哪些物品装入背包可使价值总和最大。所谓01背包,表示每一个物品只有一个,要么装入,要么不装入。
    回溯法

      01背包属于找最优解问题,用回溯法需要构造解的子集树。在搜索状态空间树时,只要左子节点是可一个可行结点,搜索就进入其左子树。对于右子树时,先计算上界函数,以判断是否将其减去,剪枝啦啦!
       上界函数bound():当前价值cw+剩余容量可容纳的最大价值<=当前最优价值bestp。
       为了更好地计算和运用上界函数剪枝,选择先将物品按照其单位重量价值从大到小排序,此后就按照顺序考虑各个物品。

    #include <stdio.h>
    #include <conio.h>
    
    int n;//物品数量
    double c;//背包容量
    double v[100];//各个物品的价值
    double w[100];//各个物品的重量
    double cw = 0.0;//当前背包重量
    double cp = 0.0;//当前背包中物品价值
    double bestp = 0.0;//当前最优价值
    double perp[100];//单位物品价值排序后
    int order[100];//物品编号
    int put[100];//设置是否装入
    
    //按单位价值排序
    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[i]=perp[i];
                perp[j]=temp;
    
                temporder=order[i];
                order[i]=order[j];
                order[j]=temporder;
                temp = v[i];
                v[i]=v[j];
                v[j]=temp;
    
                temp=w[i];
                w[i]=w[j];
                w[j]=temp;
            }
        }
    }
    
    //回溯函数
    void backtrack(int i)
    {
        double bound(int i);
        if(i>n)
        {
            bestp = cp;
            return;
        }
        if(cw+w[i]<=c)
        {
            cw+=w[i];
            cp+=v[i];
            put[i]=1;
            backtrack(i+1);
            cw-=w[i];
            cp-=v[i];
        }
        if(bound(i+1)>bestp)//符合条件搜索右子数
            backtrack(i+1);
    }
    
    //计算上界函数
    double bound(int i)
    {
        double leftw= c-cw;
        double b = 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;
        printf("请输入物品的数量和容量:");
        scanf("%d %lf",&n,&c);
        printf("请输入物品的重量和价值:");
        for(i=1;i<=n;i++)
        {
            printf("第%d个物品的重量:",i);
            scanf("%lf",&w[i]);
            printf("价值是:");
            scanf("%lf",&v[i]);
            order[i]=i;
        }
        knapsack();
        backtrack(1);
        printf("最有价值为:%lf
    ",bestp);
        printf("需要装入的物品编号是:");
        for(i=1;i<=n;i++)
        {
            if(put[i]==1)
                printf("%d ",order[i]);
        }
        return 0;
    }

    时间复杂度分析:

      上界函数bound()需要O(n)时间,在最坏的情况下有O(2^n)个右子结点需要计算上界,回溯算法backtrack需要的计算时间为O(n2^n)

  • 相关阅读:
    2-4 Vue中的属性绑定和双向数据绑定
    MySQL索引失效的几种情况
    MySQL索引失效的几种情况
    Linux实现MYSQl数据库的定时备份
    Linux实现MYSQl数据库的定时备份
    你还在 Select * 吗?
    你还在 Select * 吗?
    世界顶级的程序员们告诉你:这些书都是你应该读的
    世界顶级的程序员们告诉你:这些书都是你应该读的
    真正努力和不努力的程序员,发朋友圈究竟有什么不一样?
  • 原文地址:https://www.cnblogs.com/lixiaolun/p/4504213.html
Copyright © 2011-2022 走看看