貪心算法之背包問題


貪婪算法的基本思想:通過一系列步驟來構造問題的解,每一步都是對已構造的部分解的一個擴展,直到獲得問題的完整解。

貪婪算法中,每一步“貪婪地” 選擇最好的部分解,但不顧及這樣選擇對整體的影響(局部最優),因此得到的全局解不一定最好的解,但對許多問題它能產生整體最優解。

具體算法描述:

public static void Greedy()
        {
            float cu = c;
            int temp = 0;
            int i = 0;
            for (i = 0; i < n; ++i )
            {
                x[i] = 0;//初始化為0
            }
            for (i = 0; i < n; ++i )
            {
                temp = sortResult[i];//得到取物體的順序

                if (w[temp] > cu)
                {
                    break;
                }
                //將物品裝入背包
                x[temp] = 1;
                cu -= w[temp];//背包容量相應減小

            }
            if (i <= n)//使背包充滿
            {
                x[temp] = cu / w[temp];// 比如背包容量剩余10,w[temp] = 9.8f; 要裝滿
            }

            Display();
        }

貪婪算法每一步需要滿足3個條件:

1.可行性:即必須滿足問題的約束。

2.局部最優:它是當前步驟中所有可行選擇中最佳的局部選擇。

3.不可取消:選擇一旦做出,在后面的步驟中就無法改變。

貪心算法的基本要素:

1.貪心選擇性質:指所求問題的整體最優解可以通過一系列局部最優的選擇,即貪心選擇來達到。

2.最優子結構性質:指一個問題的最優解包含其子問題的最優解。

 

貪心算法與動態規划算法的異同:

相同點:都具有最優子結構性質。

不同點:動態規划算法通常以自底向上的方式解各子問題;而貪心算法則通常以自頂向下的方式進行;

下面研究2個經典的組合優化例題,並以此說明貪心算法與動態規划算法的主要差別。

0-1背包問題:

給定n種物品和一個背包。物品i的重量是Wi,其價值為Vi,背包的容量為C。應如何選擇裝入背包的物品,使得裝入背包中物品的總價值最大?

背包問題:
與0-1背包問題類似,所不同的是在選擇物品i裝入背包時,可以選擇物品i的一部分,而不一定要全部裝入背包,1≤i≤n。

這2類問題都具有最優子結構性質,極為相似;但背包問題可以用貪心算法求解;而0-1背包問題卻不能用貪心算法求解。

 

對於0-1背包問題:

例:n=3 , w={10,20,30} ,v={60,100,120} ,c=30
  什么是最好的部分解?  ——不求單位價值。
  按貪心法:選擇價值最大的放入 : 全部放入第3個物品,價值120。
  但這並不是最好的, 若1,2 物品的放入,總價值160。

對於背包問題:

例:n=3 w={10,20,30} v={60,100,120} c=50
單位價值:v/w={6,5,4}
因此,第一次挑一號物品全部裝入, r=40,pv=60
第二次挑2號,全部裝入r=20,pv=160
第三次挑3號,部分裝入r=0,pv=160+80=240

 

具體代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SeqListSort
{
    /// <summary>
    /// <ather>
    /// lihonlgin
    /// </ather>
    /// <content>
    /// 背包問題: 
    ///與0-1背包問題類似,所不同的是在選擇物品i裝入背包時,可以選擇物品i的一部分,而不一定要全部裝
    ///入背包,1≤i≤n。
    ///這2類問題都具有最優子結構性質,極為相似;但背包問題可以用貪心算法求解;
    ///而0-1背包問題卻不能用貪心算法求解。
    /// </content>
    /// </summary>
    class Greedy_Knapsack
    {
        const int size = 20;
        static float[] w = new float[size];
        static float[] v = new float[size];
        static int n = 5;// 十個物品
        static int c = 20;//背包容量
        static float[] x = new float[size];
        static int[] sortResult = new int[size];//保存單位價值從大到小的下標

        public static void InitData()
        {
            Random r = new Random();
            for (int i = 0; i < n; ++i )
            {
                v[i] = r.Next(10, 31);
                w[i] = r.Next(5, 16);
                x[i] = v[i] / w[i];
                Console.Write("重量為:{0:f2} ", w[i]);
                Console.WriteLine("價值為:{0:f2} ", v[i]);
            }
            Console.WriteLine();
            Sort();// 先排序
        }

        static void Sort()
        {
            float temp = 0.0f;
            int index = 0;
            int k = 0;
            for (int i = 0; i < n-1; ++i )
            {
                temp = x[i];
                index = i;
                //找到最大的效益並保存此時的下標
                for (int j = i+1; j < n; ++j )
                {
                    if (temp < x[j] && (0 == sortResult[j]))
                    {
                        temp = x[j];//第一趟比較得到最大值
                        index = j;//標記下標
                    }
                }
                //對w[i]作標記排序
                if ( 0 == sortResult[index] )
                {
                    sortResult[index] = k++;
                }
            }
            //修改效益最低的sortResult[i]標記
            for (int i = 0; i < n; i++)
            {
                if (0 == sortResult[i])
                {
                    sortResult[i] = k++;
                }
            }

        }
        
        public static void Greedy()
        {
            float cu = c;
            int temp = 0;
            int i = 0;
            for (i = 0; i < n; ++i )
            {
                x[i] = 0;//初始化為0
            }
            for (i = 0; i < n; ++i )
            {
                temp = sortResult[i];//得到取物體的順序

                if (w[temp] > cu)
                {
                    break;
                }
                //將物品裝入背包
                x[temp] = 1;
                cu -= w[temp];//背包容量相應減小

            }
            if (i <= n)//使背包充滿
            {
                x[temp] = cu / w[temp];// 比如背包容量剩余10,w[temp] = 9.8f; 要裝滿
            }

            Display();
        }

        static void Display()
        {
            for (int i = 0; i < n; ++i )
            {
                Console.Write("編號:" + i);
                Console.WriteLine("  物品放入的數量{0:F}  ", x[i]);
            }
            Console.WriteLine();
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM