动态规划 -- 01 背包问题

问题描述:

       假设现有容量m kg的背包,另外有n个物品,重量分别为w[1] w[2] ... w[i] kg,价值分别为p[1] p[2] ... p[i] (元),将哪些物品放入背包可以使得背包的总价值最大?最大价值是多少?

(示例一:m=10  n=3  重量和价值分别为 3kg-4 4kg-5 5kg-6 

1,穷举法(把所有情况列出来,比较得到 总价值最大的情况)

2、动态规划法。

 

穷举法 分析:

总物品个数为 n, 每个物品有两种可能性(拿,不拿),所以总的组合有   种;遍历这  可能性就可以找出最大价值。

怎么遍历?

不拿我们用 0 表示,不拿 用 1 表示,这时候我们可以想到用二进制来表示

(示例一:m=10  n=3  重量和价值分别为 3kg-4 4kg-5 5kg-6 

1 1 1 表示三个物品都拿

0 0 0表示三个物品都不拿

0 0 1  只拿第一个

依此类推出各种可能性

怎么知道第 i 个位置上是 0 还是 1 ?

这时候就要使用  & 运算符

  等于  1  二进制为  001          1 0 1 & 0 0 1 = 0 0 1  > 0   说明拿了第一个物品

   等于  2  二进制为  010          1 0 1 & 0 1 0 = 0 0 0  = 0  说明第二个没有拿

代码实现

class Program
    {
        static void Main(string[] args)
        {
            int[] w = { 0, 3, 4, 5 };
            int[] p = { 0, 4, 5, 6 };

            Console.WriteLine(Exhaustion(10, w, p));
            Console.WriteLine(Exhaustion(3, w, p));
            Console.WriteLine(Exhaustion(4, w, p));
            Console.WriteLine(Exhaustion(7, w, p));

            Console.ReadKey();

        }

        // 穷举法
        private static int Exhaustion(int m, int[] w, int[] p)
        {
            int len = w.Length;
            int maxPrice = 0;
            // 总共有 Math.Pow(2, len) 中可能性  
            for (int i = 0; i < Math.Pow(2, len); i++)
            {
                int tmpPrice = 0;
                int tmpW = 0;
                // 对第 i 种可能性的 获取二进制每个位置的值 ,如果是 1 就是选中  是 0 就是不选中
                for (int j = 0; j < len; j++)  
                {
                    // 获取二进制 i 上面某一位的二进制值
                    if (GetBinary(i, j) == 1)
                    {
                        tmpW += w[j];
                        tmpPrice += p[j];
                    }
                }
                if (tmpW <= m && tmpPrice > maxPrice)
                {
                    maxPrice = tmpPrice;
                }
            }
            return maxPrice;
        }
        // 获取 n 值的二进制 上的第 x 位的值
        private static int GetBinary(int n,int x)
        {
            int a = n;
            int b = (int)Math.Pow(2,x);
            int result = a & b;
            if (result == 0)
                return 0;
            return 1;
        }
    }

该方法的缺点就是 ,总个数每多一个遍历次数就是成指数增长的,性能很差。

方法二  动态规划算法   

对动态规划不熟悉的可以看下这篇 动态规划 钢条切割问题

思路:

       假设我们有 n 个物品放入容量为 m 的背包中,那么最大价值记为  c[n,m]。

       每种物品的选择有两种(拿,不拿)。

       对与 c[n,m] 有

       1、c[n,0] = c[0,m]  = 0 背包的容纳为 0 ,或者 拿 0 个物品 得到的价值都为0

       2、w[n] > m  当第n个的重量大于总量m时  c[n,m] = c[n-1,m] ,因为 最后一个(第n个)放不进去要被舍弃掉,最大价值责在              c[n - 1,m]中取得。

            若 w[n] <= m 时:

            a、放入        c[n,m] = p[n] + c[ n-1 , m - w[n] ]

            b、不放入    c[n,m] = c[n-1,m]

自顶向下法代码实现:

 class Program
    {
        static void Main(string[] args)
        {
            int[] w = { 0, 3, 4, 5 };
            int[] p = { 0, 4, 5, 6 };

            Console.WriteLine(UpDown(3, 10, w, p));
            Console.WriteLine(UpDown(3, 3, w, p));
            Console.WriteLine(UpDown(3, 4, w, p));
            Console.WriteLine(UpDown(3, 7, w, p));

            Console.ReadKey();
        }




        // 自顶向下 动态规划
        private static int UpDown(int n,int m,int[] w,int[] p)
        { 
            // c[n,0] = c[0,m]  = 0 背包的容纳为 0 ,或者 拿 0 个物品 得到的价值都为0
            if (0 == n || 0 == m) return 0;
            int maxPrice = 0;
            if (w[n] > m)
            {
                // 当第n个的重量大于总量m时  c[n,m] = c[n-1,m] ,因为 最后一个(第n个)放不进去要被舍弃掉,最大价值责在              c[n - 1,m]中取得。
                return UpDown(n - 1, m, w, p);
            }
            else // 当第n个的重量小于于总量m时
            {
                // 不放入价格    c[n,m] = c[n-1,m]
                int tmpPrice0 = UpDown(n - 1, m, w, p);
                // 放入价格      c[n,m] = p[n] + c[ n-1 , m - w[n] ]
                int tmpPrice1 = p[n] + UpDown(n - 1, m - w[n], w, p);
                // 谁大选谁
                maxPrice = tmpPrice0 > tmpPrice1 ? tmpPrice0 : tmpPrice1;
            }
            return maxPrice;
        }
    }

 接下来我们进行优化 优化思路可以参考   动态规划 钢条切割问题  我就直接上代码了

    class Program
    {
        static void Main(string[] args)
        {
            int[] w = { 0, 3, 4, 5 };
            int[] p = { 0, 4, 5, 6 };
            int m = 10; // 背包的最大容量
            int n = w.Length; // 物品总个数
            int[,] optimumSolution = new int[m + 1 , n];  

            Console.WriteLine(UpDown(3, 10, w, p, optimumSolution));
            Console.WriteLine(UpDown(3, 3, w, p, optimumSolution));
            Console.WriteLine(UpDown(3, 4, w, p, optimumSolution));
            Console.WriteLine(UpDown(3, 7, w, p, optimumSolution));

            Console.ReadKey();

        }

        // 自顶向下 动态规划
        private static int UpDown(int n, int m, int[] w, int[] p, int[,] optimumSolution)
        { 
            // c[n,0] = c[0,m]  = 0 背包的容纳为 0 ,或者 拿 0 个物品 得到的价值都为0
            if (0 == n || 0 == m) return 0;
            if (optimumSolution[m,n] != 0)
            {
                return optimumSolution[m,n];
            }
            int maxPrice = 0;
            if (w[n] > m)
            {
                // 当第n个的重量大于总量m时  c[n,m] = c[n-1,m] ,因为 最后一个(第n个)放不进去要被舍弃掉,最大价值在 c[n - 1,m]中取得。
                if (optimumSolution[m,n - 1] == 0) 
                    optimumSolution[m,n] = UpDown(n - 1, m, w, p, optimumSolution);
                return optimumSolution[m, n];
            }
            else // 当第n个的重量小于于总量m时
            {
                // 不放入价格    c[n,m] = c[n-1,m]
                int tmpPrice0 = 0;
                // 放入价格      c[n,m] = p[n] + c[ n-1 , m - w[n] ]
                int tmpPrice1 = 0;

                if (optimumSolution[m, n - 1] == 0)
                    optimumSolution[m, n] = UpDown(n - 1, m, w, p, optimumSolution);
                if (optimumSolution[m - w[n], n - 1] == 0)
                    optimumSolution[m - w[n], n - 1] = UpDown(n - 1, m - w[n], w, p, optimumSolution);

                tmpPrice0 = optimumSolution[m,n - 1];
                tmpPrice1 = p[n] + optimumSolution[m - w[n],n - 1];

                // 谁大选谁
                maxPrice = tmpPrice0 > tmpPrice1 ? tmpPrice0 : tmpPrice1;
            }
            return maxPrice;
        }
    }

自底向上法代码实现:

    class Program
    {
        static void Main(string[] args)
        {
            int[] w = { 0, 3, 4, 5 };
            int[] p = { 0, 4, 5, 6 };
            int m = 10; // 背包的最大容量
            int n = w.Length; // 物品总个数
            int[,] optimumSolution = new int[m + 1 , n];  

            Console.WriteLine(ButtomUp(10, w, p, optimumSolution));
            Console.WriteLine(ButtomUp(3, w, p, optimumSolution));
            Console.WriteLine(ButtomUp(4, w, p, optimumSolution));
            Console.WriteLine(ButtomUp(7, w, p, optimumSolution));

            Console.ReadKey();

        }

        private static int ButtomUp(int m, int[] w, int[] p, int[,] optimumSolution)
        {
            if (0 == m) return 0;
            for (int i = 1; i <= m; i++)
            {
                int tmpM = i;
                for (int j = 1; j < w.Length; j++)
                {
                    if (w[j] > tmpM)
                    {
                        optimumSolution[tmpM, j] = optimumSolution[tmpM , j - 1];
                    }
                    else
                    { 
                        // 不放入
                        int tmpPrice0 = optimumSolution[tmpM, j - 1];
                        // 放入
                        int tmpPrice1 = p[j] + optimumSolution[tmpM - w[j], j - 1];

                        if (tmpPrice0 > tmpPrice1)
                        {
                            optimumSolution[tmpM, j] = tmpPrice0;
                        }
                        else
                        {
                            optimumSolution[tmpM, j] = tmpPrice1;
                        }
                    }
                }
            }
            return optimumSolution[m, w.Length - 1];
        }
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值