01揹包問題理解動態規劃算法

一.動態規劃算法算法

簡單理解:在一些分治算法解決的問題中,須要將較大規模的問題轉化爲較小規模的問題,每每會用到遞歸。可是在一些問題中,遞歸的小問題被屢次重複運算,浪費了性能,所以可使用數組或者其餘合適的方式將運算過的小規模問題的結果記錄下來,再運算小規模的問題時先看是否是已經運算過了,沒有運算過再去運算並將結果保存,因此通常分治算法都是從大規模問題開始遞歸運算,不斷將問題規模變小,而動態規劃算法則通常從小規模問題開始運算,每次運算分解出的小規模問題都是已經運算過的。如此便使用了存儲空間換取了運算時間,減少了時間複雜度。數組

二.01揹包問題函數

 

 1.窮舉法性能

能夠先不考慮揹包,考慮被拿走的物品的全部組合狀況,n個物品有2n種拿法(每一個物品均可以選擇拿或者不拿),而後再判斷每一種拿法是否能放入揹包,能放入揹包的狀況下計算總價值並比較出最大價值。spa

        static void Main(string[] args)
        {
            //記錄重量的數組,物品有3種重量,分別是三、四、5
            int[] w = { 0, 3, 4, 5 };
            //記錄物品價值的數組,和重量數組對應,物品價值分別是四、五、6
            int[] p = { 0, 4, 5, 6 };
            //調用Exhaustivity函數獲得7kg的揹包放置物品的最大價值
            Console.WriteLine(Exhaustivity(9 , w, p));
            Console.ReadKey();
        }
    
        /// <summary>
        /// 計算給定的mkg揹包放置物品的最大價值
        /// </summary>
        /// <param name="m">給定的物品重量</param>
        /// <param name="w">記錄全部物品重量的數組</param>
        /// <param name="p">記錄全部物品對應價格的數組</param>
        /// <returns>揹包中放置物品的最大價值</returns>
        public static int Exhaustivity(int m,int[] w,int[] p)
        {
            //記錄放入物品的最大的價格
            int maxPrice = 0;
            //有3個物品,外層就循環2的3次方,即i值從0取到7,i的二進制數字最多3位,每個i的取值的二進制數字都對應3個物品是否放入揹包
            //如i爲6,二進制爲110,表明取前兩個物品,不取第3個物品
            //又如i爲0,二進制爲000,表明3個物品均不取;i爲1,二進制001,表明只取第3個物品
            //經過這種方式窮舉全部物品放入揹包的狀況,而後判斷每一種狀況重量是否知足要求,再比較價格
            for(int i = 0;i < Math.Pow(2,w.Length - 1); i++)
            {
                //記錄全部被放入揹包物品的總重量
                int weightTotal = 0;
                //記錄全部被放入揹包物品的總價格
                int priceTotal = 0;
                //內層循環負責計算並比較全部物品的總重量和總價格
                //內層循環的次數就是物品個數,而後分別判斷當前狀況下每一個物品是否放入揹包,j表明第幾個物品
                for(int j = 1;j <= w.Length - 1; j++)
                {
                    //計算當前物品是否放入揹包
                    int result = Get2(i, j);
                    //在放入揹包的狀況下,將重量和價格累加到總重量和總價格上
                    if(result == 1)
                    {
                        weightTotal += w[j];
                        priceTotal += p[j];
                    }
                }
                //判斷是否超重,不超重的狀況下判斷價格是否是大於最大價格,若是是更新最大價格
                if (weightTotal <= m && priceTotal > maxPrice)
                    maxPrice = priceTotal;
            }
            //返回最大價格
            return maxPrice;
        }
        /// <summary>
        /// 經過按位與運算獲得給定物品number在給定放入狀況i中是否放入揹包
        /// </summary>
        /// <param name="i">給定的物品放入狀況</param>
        /// <param name="number">給定的物品編號</param>
        /// <returns></returns>
        public static int Get2(int i,int number)
        {
            int A = i;
            int B = (int)Math.Pow(2,number - 1);
            int result = A & B;
            if (result == 0)
                return 0;
            return 1;
        }

 

 2.分治算法(自上而下分解問題)3d

 這個問題的分治算法的核心是將物品逐個放入揹包,有如下幾種狀況:code

1)揹包沒有剩餘重量了blog

2)物品過重,沒法放入揹包遞歸

3)物品的編號爲0,即全部的物品都試過了string

4)物品還能夠放入揹包

在第4)種狀況下,有兩種狀況,一種放入物品獲得的價格最大,一種不放入物品獲得的價格最大,所以須要進行比較獲得最大價格

        static void Main(string[] args)
        {
            //記錄重量的數組,物品有3種重量,分別是三、四、5
            int[] w = { 0, 3, 4, 5 };
            //記錄物品價值的數組,和重量數組對應,物品價值分別是四、五、6
            int[] p = { 0, 4, 5, 6 };
            Console.WriteLine(UpDown(9, w.Length - 1, w, p));
            Console.ReadKey();
        }
    
        /// <summary>
        /// 根據揹包剩餘重量和物品編號計算放入物品能得到最大價值仍是不放入物品能得到最大價值
        /// </summary>
        /// <param name="m">揹包剩餘能裝的重量</param>
        /// <param name="i">當前放入揹包的物品編號</param>
        /// <param name="w">記錄全部物品重量的數組</param>
        /// <param name="p">記錄全部物品價格的數組</param>
        /// <returns>放入揹包的物品的最大價格</returns>
        public static int UpDown(int m,int i,int[] w,int[] p)
        {
            //若是揹包還能裝的重量爲0或者放入的物品編號爲0,價格天然都是0
            if (i == 0 || m == 0) return 0;
            //將第i個物品放入揹包,可是物品放入後超重,這種狀況下就不放入這個物品,看下一個物品是否能放入揹包(物品編號-1)
            if(w[i] > m)
                return UpDown(m,i - 1, w, p);
            //在第i個物品能放入揹包的狀況下須要計算比較價格
            else
            {
                //計算並記錄將物品放入揹包的狀況下的最大價格(遞歸物品編號-1,從揹包剩餘重量中扣除當前物品重量,因爲當前物品放入揹包,揹包中物品價格加上當前物品價格)
                int maxValue1 = UpDown(m - w[i], i - 1, w, p) + p[i];
                //計算並記錄不將物品放入揹包的狀況下的最大價格(遞歸物品編號-1,可是因爲沒有放入物品,揹包剩餘重量不變)
                int maxValue2 = UpDown(m, i - 1, w, p);
                //比較並取其中價格最大的那個
                if (maxValue1 > maxValue2)
                    return maxValue1;
                return maxValue2;
            }
        }

 

 3.改進分治算法,使用數組記錄已經運算過的問題結果

        //定義一個規則二維數組記錄結果,行數對應揹包重量,列數對應放入的物品個數,值對應這種狀況揹包中的最大重量
        public static int[,] result = new int[11, 4];
        static void Main(string[] args)
        {
            int[] w = { 0, 3, 4, 5 };
            int[] p = { 0, 4, 5, 6 };
            Console.WriteLine(UpDown(9, w.Length - 1, w, p));
            Console.ReadKey();
        }

        public static int UpDown(int m,int i,int[] w,int[] p)
        {
            if (i == 0 || m == 0) return 0;
            //判斷這種狀況是否是已經計算過了,計算過了就直接返回
            if (result[m, i] != 0)
                return result[m, i];

            //在計算完成後須要將計算結果存儲起來
            if(w[i] > m)
                return result[m,i] = 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 result[m, i] = maxValue1;
                return result[m, i] = maxValue2;
            }
        }

4.動態規劃算法(自下而上計算問題)

        static void Main(string[] args)
        {
            int[] w = { 0, 3, 4, 5 };
            int[] p = { 0, 4, 5, 6 };
            Console.WriteLine(BottomUp(9, w.Length - 1, w, p));
            Console.ReadKey();
        }
        //記錄運算結果的數組,行數表明揹包重量,列數表明物品編號
        public static int[,] result = new int[11, 4];
        /// <summary>
        /// 計算指定重量m的揹包种放置前i個物品放置的最大價值
        /// </summary>
        /// <param name="m">揹包重量</param>
        /// <param name="i">物品編號</param>
        /// <param name="w">記錄全部物品重量的數組</param>
        /// <param name="p">記錄全部物品價格的數組</param>
        /// <returns></returns>
        public static int BottomUp(int m,int i,int[] w,int[] p)
        {
            //外層循環從1到m,揹包重量從小到大
            for(int tempM = 1;tempM <= m; tempM++)
            {
                //內層循環從1到i,物品個數從1到i
                for(int tempI = 1;tempI <= i;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];
                        result[tempM, tempI] = maxValue1 > maxValue2 ? maxValue1 : maxValue2;
                    }
                }
            }
            //自下而上計算完成後,返回重量爲m的揹包放入i個物品的最大價格便可
            return result[m, i];
        }
相關文章
相關標籤/搜索