Bootstrap

动态规划--0-1背包问题

动态规划–0-1背包问题

问题描述:
假设现有容量m kg的背包,另外有 i 个物品,重量分别为w[1] w[2]…w[i] (kg),价值分别为p[1]、p[2]…p[i] (元),将哪些物品放入背包可以使得背包的总价值最大?最大价值是多少?
(示例一:m=10 i=3重量和价值分别为3kg-4元,4kg-5元 ,5kg-6元)

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

  1. 如果容景增大,物品增多,这个方法的运行时间将成指数增长
  2. 每一个物品都有两种状态(放与不放,0和1状态),所以如果有n个物品,就有2的n次方种情况
internal class Program
{
   static void Main(string[] args)
   {
       int m;
       int[] w = { 0, 3, 4, 5 };
       int[] p = { 0, 4, 5, 6 };

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

   }

   public static int Exhaustivity(int m, int[] w,int[] p)
   {
       int i = w.Length - 1;//物品的个数
       int maxPrice = 0;
       for(int j = 0; j < Math.Pow(2,m); j++)
       {
           //取得j 上某一个位的二进制值
           int weightTotal =  0;
           int priceTotal = 0;
           for(int number = 1; number <= i; number++)
           {
               int result = Get2(j, number);
               if (result == 1)
               {
                   weightTotal += w[number];
                   priceTotal += p[number];
               }
           }
           if(weightTotal <= m && priceTotal > maxPrice)
           {
               maxPrice = priceTotal;
           }
       }
       return maxPrice;
   }

   public static int Get2(int j, int number)
   {
       int A = j;
       int B = (int)Math.Pow(2,number - 1);

       int result = A & B;
       if(result == 0)
       {
           return 0;
       }
       return 1;
   }
}

动态规划算法

我们要求得 i 个物体放入容量为m(kg)的背包的最大价值(记为c[i,m])。在选择物品的时候,对于每种物品只有两种选择,即装入背包或不装入背包。某种物品不能装入多次(可以认为每种物品只有一个),因此该问题被称为0-1背包问题

对于c[i,m]有下面几种情况:

  1. c[i,0]=c[0,m]=0
  2. 当w[i]>m时 ,c[i,m]=c[i-1,m] (最后一个物品的重量大于容量,直接舍弃不用)
  3. 当w[i]<=m的时候有两种情况,一种是放入i,一种是不放入i
    • 不放入i,c[i,m]=c[i-1,m]
    • 放入i ,c[i,m]=c[i-1,m-w[i]+p[i]
    • c[i,m]=max(不放入i,放入i)

01背包问题递归实现(不带备忘录的自顶向下法)

internal class Program
{
    static void Main(string[] args)
    {
        int m;
        int[] w = { 0, 3, 4, 5 };
        int[] p = { 0, 4, 5, 6 };
        Console.WriteLine(UpDown(10,3,w,p));
        Console.WriteLine(UpDown(3,3,w,p));
        Console.WriteLine(UpDown(4,3,w,p));
        Console.WriteLine(UpDown(5,3,w,p));
        Console.WriteLine(UpDown(7,3,w,p));

    }

    //m是背包容量
    //i是物品个数
    //wp是物品的重量和价值的数组

    public static int UpDown(int m, int i,int[] w, int[] p ) //返回值是m可以存储的最大价值
    {
        
        if (i == 0||m== 0)
        {
            return 0;
        }
        if (w[i] > m)
        {
            return UpDown(m,i-1,w, p );
        }
        else
        {
            int maxValue1 = UpDown(m - w[i], i - 1, w, p ) + p[i];
            int maxValue2 = UpDown(m, i - 1, w, p );
            if (maxValue1 > maxValue2)
            {
                return maxValue1;
            }
            return maxValue2;

        }
    }
}

01背包问题-递归实现(带备忘的自顶向下法)

internal class Program
{
    public static int[,] result = new int[11, 4];
    static void Main(string[] args)
    {
        int m;
        int[] w = { 0, 3, 4, 5 };
        int[] p = { 0, 4, 5, 6 };
        Console.WriteLine(UpDown(10, 3, w, p));
        Console.WriteLine(UpDown(3, 3, w, p));
        Console.WriteLine(UpDown(4, 3, w, p));
        Console.WriteLine(UpDown(5, 3, w, p));
        Console.WriteLine(UpDown(7, 3, w, p));

    }

    //m是背包容量
    //i是物品个数
    //wp是物品的重量和价值的数组

    public static int UpDown(int m, int i, int[] w, int[] p) //返回值是m可以存储的最大价值
    {

        if (i == 0 || m == 0)
        {
            return 0;
        }

        if (result[m,i]!=0)
        {
            return result[m,i];
        }

        if (w[i] > m)
        {
            result[m,i] = UpDown(m, i - 1, w, p);
            return result[m,i];
        }
        else
        {
            int maxValue1 = UpDown(m - w[i], i - 1, w, p) + p[i];
            int maxValue2 = UpDown(m, i - 1, w, p);
            if (maxValue1 > maxValue2)
            {
                result[m, i] = maxValue1;
            }
            else
            {
                result[m,i] = maxValue2;
            }
            return result[m,i];

        }
    }
}

01背包问题(自底向上法)

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

        Console.WriteLine(BottomUp(10,3, w, p));
        Console.WriteLine(BottomUp(3, 3, w, p));
        Console.WriteLine(BottomUp(4, 3, w, p));
        Console.WriteLine(BottomUp(5, 3, w, p));
        Console.WriteLine(BottomUp(7, 3, w, p));
    }

    public static int[,] result = new int[11, 4];
    public static int BottomUp(int m,int i, int[] w, int[] p)
    {
        if (result[m,i] != 0)return result[m,i];
        for (int tempM = 1; tempM < m+1; tempM++)
        {
             for(int tempI = 1; tempI <i+1;tempI++)
            {
                if (result[tempM,tempI] != 0 )continue;
                if (w[tempI] >tempM)
                {
                    result[tempM, tempI] = result[tempM, tempI - 1];
                }
                else
                {
                    int maxValue1 = result[tempM - w[tempI], tempI-1]+p[tempI];
                    int maxValue2 = result[tempM, tempI - 1];
                    if (maxValue1 > maxValue2)
                    {
                        result[tempM, tempI] = maxValue1;
                    }
                    else
                    {
                        result[tempM,tempI] = maxValue2;
                    }
                }
            }
        }
        return result[m,i];
          
    }
}
;