貪心算法大學的時候就已經學過也弄過,可能周末確實沒想到寫什么,就順手學了當年學習的知識,貪心算法(也稱為貪婪算法),貪心算法總是作出在當前看來最好的選擇。貪心算法並不從整體最優考慮,它所作出的選擇只是在某種意義上的局部最優選擇。當然,希望貪心算法得到的最終結果也是整體最優的。雖然貪心算法不能對所有問題都得到整體最優解,但對許多問題它能產生整體最優解。
貪心要素
概念就是這樣,如果需要詳情可繼續搜索獲取更多信息,這個時候出現了一個問題,什么使用貪心算法?只需要滿足兩點即可,首先就是所求解的問題最優解可以一系列局部最優來達到解決,其次一個問題的最優解中是否包含一個子問題的最優解,也稱為最優子結構,一般如果包含子問題的最優解可以通過動態算法或者貪心算法來求解。簡單說就是貪心策略適用的前提是:局部最優策略能導致產生全局最優解。一般的解題框架:
從問題的某一初始解出發;
貪心Demo
貪心Demo這哥們大一就出現在一個很經典的C語言題目中,背包問題,有一個背包,背包容量是M=150。有7個物品,物品可以分割成任意大小。要求盡可能讓裝入背包中的物品總價值最大,但不能超過總容量。(跟0-1背包不同,0-1需要使用到動態規划)
物品 A B C D E F G
重量 35 30 60 50 40 10 25
價值 10 40 30 50 35 40 30
解題思路:
約束條件是裝入的物品總重量不超過背包容量:∑wi<=M( M=150)。
(1)根據貪心的策略,每次挑選價值最大的物品裝入背包,得到的結果是否最優?
(2)每次挑選所占重量最小的物品裝入是否能得到最優解?
(3)每次選取單位重量價值最大的物品,成為解本題的策略。
就是這個猜想一下然后需要證明的,能看博客的基本上也明白第三種是最優的答案,一般這種題晚上有C代碼,C++代碼,C#代碼比較少,我沒事寫了寫,將就看下,定義一個物品的類:
public class Product { public string Name { get; set; } public float Weight { get; set; } public float Value { get; set; } public float UnitValue { get; set; } }
控制台代碼,代碼比較簡單:
float[] weight = new float[] { 35, 30, 60, 50, 40, 15, 20 }; float[] value = new float[] { 10, 40, 30, 50, 35, 40, 30 }; string[] name = new string[] { "A", "B", "C", "D", "E", "F", "G" }; List<Product> list = new List<Product>(); for (int i = 0; i < weight.Length; i++) { Product product = new Product(); product.Name = name[i]; product.Weight = weight[i]; product.Value = value[i]; product.UnitValue = value[i] / weight[i]; list.Add(product); } float sum = 0; foreach (Product item in list) { Console.Write(item.Name + "-" + item.UnitValue+"\t"); } List<Product> result = new List<Product>(); foreach (var product in list.OrderByDescending(item => item.UnitValue)) { sum += product.Weight; if (sum > 150) break; result.Add(product); } foreach (var product in result) { Console.Write(product.Name + "--" + product.Weight+"\t"); } Console.WriteLine(); Console.WriteLine("總價值:" + result.Sum(item => item.Value) + "\t總重量:" + result.Sum(item => item.Weight)); Console.ReadKey();
結果如下:
這樣求出的最優結果是物品沒有分割的情況的,有的是要求切割的,結果如何就看怎么算了;
背包算是過去的,還有一個是繞不過的會議,老師講課的還經常拿上課的教師做例子,先看下題目:
設有N個活動時間集合,每個活動都要使用同一個資源,比如說會議場,而且同一時間內只能有一個活動使用,每個活動都有一個使用活動的開始si和結束時間fi,即他的使用區間為(si,fi),現在要求你分配活動占用時間表,即哪些活動占用該會議室,哪些不占用,使得他們不沖突,要求是盡可能多的使參加的活動最大化,即所占時間區間最大化~
看着費勁的話那就看張網絡圖片吧,i表示活動,S[i]開始時間,f[i]表示結束時間:
這個題目也很簡單,需要想清楚的一點的就是如果兩個活動需要相容,那么第二個活動的開始時間一定要大於等於第一個活動的結束時間,想清楚了這個就OK了;
int[] start = { 1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12 }; int[] end = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; List<int> list = new List<int>() { 0 }; int j = 0; for (int i = 1; i < start.Length; i++) { if (start[i] >= end[j]) { list.Add(i); j = i; } } for (int i = 0; i < list.Count(); i++) { Console.Write(list[i].ToString()+"\t"); } Console.ReadKey();
上面問題的答案是0,3,7,10;這個問題還有一個類似的兄弟問題就是如何求解同一條直線各個線段覆蓋的長度,具體的就將上圖的作為參考吧,詳細數值看下面代碼即可:
int[] start = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; int[] end = { 3, 5, 7, 6, 9, 8, 12, 10, 13, 15 }; int j = 0; int sum = end[0] - start[0]; for (int i = 1; i < start.Length; i++) { if (start[i] >= end[j]) { sum += end[i] - start[i]; j = i; } else { if (end[i] > end[j]) { sum += end[i] - end[j]; j = i; } } } Console.WriteLine("總的里程數:" + sum.ToString()); Console.ReadKey();
答案是13公里~小算怡情,大算傷身,強算灰灰湮滅,我就小小算算~各位,晚安~