導航:首頁 > 源碼編譯 > 貪心演算法可視化編程

貪心演算法可視化編程

發布時間:2023-08-26 08:29:23

Ⅰ 貪婪演算法幾個經典例子

問題一:貪心演算法的例題分析 例題1、[0-1背包問題]有一個背包,背包容量是M=150。有7個物品,物品不可以分割成任意大小。要求盡可能讓裝入背包中的物品總價值最大,但不能超過總容量。物品 A B C D E F G重量 35kg 30kg 6kg 50kg 40kg 10kg 25kg價值 10$ 40$ 30$ 50$ 35$ 40$ 30$分析:目標函數:∑pi最大約束條件是裝入的物品總重量不超過背包容量:∑wi 64輸出一個解,返回上一步驟c--(x,y) ← c計算(x,y)的八個方位的子結點,選出那些可行的子結點循環遍歷所有可行子結點,步驟c++重復2顯然⑵是一個遞歸調用的過程,大致如下:C++程序: #define N 8void dfs(int x,int y,int count){ int i,tx,ty; if(count>N*N) { output_solution();輸出一個解 return; } for(i=0; i>

問題二:收集各類貪心演算法(C語言編程)經典題目 tieba./...&tb=on網路的C語言貼吧。 全都是關於C的東西。

問題三:幾種經典演算法回顧 今天無意中從箱子里發現了大學時學演算法的教材《演算法設計與分析》,雖然工作這么幾年沒在什麼地方用過演算法,但演算法的思想還是影響深刻的,可以在系統設計時提供一些思路。大致翻了翻,重溫了一下幾種幾種經典的演算法,做一下小結。分治法動態規劃貪心演算法回溯法分支限界法分治法1)基本思想將一個問題分解為多個規模較小的子問題,這些子問題互相獨立並與原問題解決方法相同。遞歸解這些子問題,然後將這各子問題的解合並得到原問題的解。2)適用問題的特徵該問題的規模縮小到一定的程度就可以容易地解決該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子問題3)關鍵如何將問題分解為規模較小並且解決方法相同的問題分解的粒度4)步驟分解->遞歸求解->合並 divide-and-conquer(P) { if ( | P | >

問題四:求三四個貪心演算法的例題(配源程序代碼,要帶說明解釋的)!非常感謝 貪心演算法的名詞解釋
ke./view/298415
第一個貪心演算法 (最小生成樹)
ke./view/288214
第二個貪心演算法 (Prim演算法)
ke./view/671819
第三個貪心演算法 (kruskal演算法)
ke./view/247951
演算法都有詳細解釋的

問題五:求 Java 一些經典例子演算法 前n項階乘分之一的和
public class jiecheng {
public static void main(String[] args)
{
double sum=0;
double j=1;
int n=10;
for(int i=1;i 問題六:關於編程的貪心法 定義
所謂貪心演算法(又稱貪婪演算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。 貪心演算法不是對所有問題都能得到整體最優解,但對范圍相當廣泛的許多問題他能產生整體最優解或者是整體最優解的近似解。
[編輯本段]貪心演算法的基本思路
1.建立數學模型來描述問題。 2.把求解的問題分成若干個子問題。 3.對每一子問題求解,得到子問題的局部最優解。 4.把子問題的解局部最優解合成原來解問題的一個解。 實現該演算法的過程: 從問題的某一初始解出發; while 能朝給定總目標前進一步 do 求出可行解的一個解元素; 由所有解元素組合成問題的一個可行解。 下面是一個可以試用貪心演算法解的題目,貪心解的確不錯,可惜不是最優解。
[編輯本段]例題分析
[背包問題]有一個背包,背包容量是M=150。有7個物品,物品不可以分割成任意大小。 要求盡可能讓裝入背包中的物品總價值最大,但不能超過總容量。 物品 A B C D E F G 重量 35 30 60 50 40 10 25 價值 10 40 30 50 35 40 30 分析: 目標函數: ∑pi最大 約束條件是裝入的物品總重量不超過背包容量:∑wi>

問題七:求解一貪心演算法問題 最快回答那個不懂別亂說,別誤人子弟。
這題標準的貪心演算法,甚至很多時候被當做貪心例題
要求平均等待時間,那麼就得用 總等待時間 / 人數
所以只用關心總等待時間,
如果數據大的在前面,那麼後面必然都要加一次這個時間,所以按從小到大排。
給你寫了個,自己看吧。
#include stdafx.h
#include
#include
#include
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
int n;
float arr[105];
cin >> n;
for(int i = 0; i > arr[i];
sort(arr, arr+n);
int tnow = 0;
int tmax = 0;
for(int i = 0; i 問題八:分治演算法的應用實例 下面通過實例加以說明: 給你一個裝有1 6個硬幣的袋子。1 6個硬幣中有一個是偽造的,並且那個偽造的硬幣比真的硬幣要輕一些。你的任務是找出這個偽造的硬幣。為了幫助你完成這一任務,將提供一台可用來比較兩組硬幣重量的儀器,利用這台儀器,可以知道兩組硬幣的重量是否相同。比較硬幣1與硬幣2的重量。假如硬幣1比硬幣2輕,則硬幣1是偽造的;假如硬幣2比硬幣1輕,則硬幣2是偽造的。這樣就完成了任務。假如兩硬幣重量相等,則比較硬幣3和硬幣4。同樣,假如有一個硬幣輕一些,則尋找偽幣的任務完成。假如兩硬幣重量相等,則繼續比較硬幣5和硬幣6。按照這種方式,可以最多通過8次比較來判斷偽幣的存在並找出這一偽幣。另外一種方法就是利用分而治之方法。假如把1 6硬幣的例子看成一個大的問題。第一步,把這一問題分成兩個小問題。隨機選擇8個硬幣作為第一組稱為A組,剩下的8個硬幣作為第二組稱為B組。這樣,就把1 6個硬幣的問題分成兩個8硬幣的問題來解決。第二步,判斷A和B組中是否有偽幣。可以利用儀器來比較A組硬幣和B組硬幣的重量。假如兩組硬幣重量相等,則可以判斷偽幣不存在。假如兩組硬幣重量不相等,則存在偽幣,並且可以判斷它位於較輕的那一組硬幣中。最後,在第三步中,用第二步的結果得出原先1 6個硬幣問題的答案。若僅僅判斷硬幣是否存在,則第三步非常簡單。無論A組還是B組中有偽幣,都可以推斷這1 6個硬幣中存在偽幣。因此,僅僅通過一次重量的比較,就可以判斷偽幣是否存在。假設需要識別出這一偽幣。把兩個或三個硬幣的情況作為不可再分的小問題。注意如果只有一個硬幣,那麼不能判斷出它是否就是偽幣。在一個小問題中,通過將一個硬幣分別與其他兩個硬幣比較,最多比較兩次就可以找到偽幣。這樣,1 6硬幣的問題就被分為兩個8硬幣(A組和B組)的問題。通過比較這兩組硬幣的重量,可以判斷偽幣是否存在。如果沒有偽幣,則演算法終止。否則,繼續劃分這兩組硬幣來尋找偽幣。假設B是輕的那一組,因此再把它分成兩組,每組有4個硬幣。稱其中一組為B1,另一組為B2。比較這兩組,肯定有一組輕一些。如果B1輕,則偽幣在B1中,再將B1又分成兩組,每組有兩個硬幣,稱其中一組為B1a,另一組為B1b。比較這兩組,可以得到一個較輕的組。由於這個組只有兩個硬幣,因此不必再細分。比較組中兩個硬幣的重量,可以立即知道哪一個硬幣輕一些。較輕的硬幣就是所要找的偽幣。 在n個元素中找出最大元素和最小元素。我們可以把這n個元素放在一個數組中,用直接比較法求出。演算法如下:void maxmin1(int A[],int n,int *max,int *min){ int i;*min=*max=A[0];for(i=0;i *max) *max= A[i];if(A[i] >

問題九:回溯演算法的典型例題 八皇後問題:在8×8格的國際象棋上擺放八個皇後,使其不能互相攻擊,即任意兩個皇後都不能處於同一行、同一列或同一斜線上,問有多少種擺法。

問題十:什麼是演算法,都什麼,舉個例子,謝謝 演算法就是解決問題的具體的方法和步驟,所以具有以下性質:
1、有窮性: 一個演算法必須保證執行有限步之後結束(如果步驟無限,問題就無法解決)
2、確切性:步驟必須明確,說清楚做什麼。
3、輸入:即解決問題前我們所掌握的條件。
4、輸出:輸出即我們需要得到的答案。
5、可行性:邏輯不能錯誤,步驟必須有限,必須得到結果。
演算法通俗的講:就是解決問題的方法和步驟。在計算機發明之前便已經存在。只不過在計算機發明後,其應用變得更為廣泛。通過簡單的演算法,利用電腦的計算速度,可以讓問題變得簡單。

Ⅱ 演算法怎麼學

貪心演算法的定義:

貪心演算法是指在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,只做出在某種意義上的局部最優解。貪心演算法不是對所有問題都能得到整體最優解,關鍵是貪心策略的選擇,選擇的貪心策略必須具備無後效性,即某個狀態以前的過程不會影響以後的狀態,只與當前狀態有關。

解題的一般步驟是:

1.建立數學模型來描述問題;

2.把求解的問題分成若干個子問題;

3.對每一子問題求解,得到子問題的局部最優解;

4.把子問題的局部最優解合成原來問題的一個解。

如果大家比較了解動態規劃,就會發現它們之間的相似之處。最優解問題大部分都可以拆分成一個個的子問題,把解空間的遍歷視作對子問題樹的遍歷,則以某種形式對樹整個的遍歷一遍就可以求出最優解,大部分情況下這是不可行的。貪心演算法和動態規劃本質上是對子問題樹的一種修剪,兩種演算法要求問題都具有的一個性質就是子問題最優性(組成最優解的每一個子問題的解,對於這個子問題本身肯定也是最優的)。動態規劃方法代表了這一類問題的一般解法,我們自底向上構造子問題的解,對每一個子樹的根,求出下面每一個葉子的值,並且以其中的最優值作為自身的值,其它的值舍棄。而貪心演算法是動態規劃方法的一個特例,可以證明每一個子樹的根的值不取決於下面葉子的值,而只取決於當前問題的狀況。換句話說,不需要知道一個節點所有子樹的情況,就可以求出這個節點的值。由於貪心演算法的這個特性,它對解空間樹的遍歷不需要自底向上,而只需要自根開始,選擇最優的路,一直走到底就可以了。

話不多說,我們來看幾個具體的例子慢慢理解它:

1.活動選擇問題

這是《演算法導論》上的例子,也是一個非常經典的問題。有n個需要在同一天使用同一個教室的活動a1,a2,…,an,教室同一時刻只能由一個活動使用。每個活動ai都有一個開始時間si和結束時間fi 。一旦被選擇後,活動ai就占據半開時間區間[si,fi)。如果[si,fi]和[sj,fj]互不重疊,ai和aj兩個活動就可以被安排在這一天。該問題就是要安排這些活動使得盡量多的活動能不沖突的舉行。例如下圖所示的活動集合S,其中各項活動按照結束時間單調遞增排序。

關於貪心演算法的基礎知識就簡要介紹到這里,希望能作為大家繼續深入學習的基礎。

Ⅲ 關於編程的貪心法

定義
所謂貪心演算法(又稱貪婪演算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。 貪心演算法不是對所有問題都能得到整體最優解,但對范圍相當廣泛的許多問題他能產生整體最優解或者是整體最優解的近似解。
[編輯本段]貪心演算法的基本思路
1.建立數學模型來描述問題。 2.把求解的問題分成若干個子問題。 3.對每一子問題求解,得到子問題的局部最優解。 4.把子問題的解局部最優解合成原來解問題的一個解。 實現該演算法的過程: 從問題的某一初始解出發; while 能朝給定總目標前進一步 do 求出可行解的一個解元素; 由所有解元素組合成問題的一個可行解。 下面是一個可以試用貪心演算法解的題目,貪心解的確不錯,可惜不是最優解。
[編輯本段]例題分析
[背包問題]有一個背包,背包容量是M=150。有7個物品,物品不可以分割成任意大小。 要求盡可能讓裝入背包中的物品總價值最大,但不能超過總容量。 物品 A B C D E F G 重量 35 30 60 50 40 10 25 價值 10 40 30 50 35 40 30 分析: 目標函數: ∑pi最大 約束條件是裝入的物品總重量不超過背包容量:∑wi<=M( M=150) (1)根據貪心的策略,每次挑選價值最大的物品裝入背包,得到的結果是否最優? (2)每次挑選所佔重量最小的物品裝入是否能得到最優解? (3)每次選取單位重量價值最大的物品,成為解本題的策略。 值得注意的是,貪心演算法並不是完全不可以使用,貪心策略一旦經過證明成立後,它就是一種高效的演算法。 貪心演算法還是很常見的演算法之一,這是由於它簡單易行,構造貪心策略不是很困難。 可惜的是,它需要證明後才能真正運用到題目的演算法中。 一般來說,貪心演算法的證明圍繞著:整個問題的最優解一定由在貪心策略中存在的子問題的最優解得來的。 對於例題中的3種貪心策略,都是無法成立(無法被證明)的,解釋如下: (1)貪心策略:選取價值最大者。 反例: W=30 物品:A B C 重量:28 12 12 價值:30 20 20 根據策略,首先選取物品A,接下來就無法再選取了,可是,選取B、C則更好。 (2)貪心策略:選取重量最小。它的反例與第一種策略的反例差不多。 (3)貪心策略:選取單位重量價值最大的物品。 反例: W=30 物品:A B C 重量:28 20 10 價值:28 20 10 根據策略,三種物品單位重量價值一樣,程序無法依據現有策略作出判斷,如果選擇A,則答案錯誤。 【注意:如果物品可以分割為任意大小,那麼策略3可得最優解】 對於選取單位重量價值最大的物品這個策略,可以再加一條優化的規則:對於單位重量價值一樣的,則優先選擇重量小的!這樣,上面的反例就解決了。 但是,如果題目是如下所示,這個策略就也不行了。 W=40 物品:A B C 重量:28 20 15 價值:28 20 15 附:本題是個NP問題,用貪心法並不一定可以求得最優解,以後了解了動態規劃演算法後本題就有了新的解法。
[編輯本段]備注
貪心演算法當然也有正確的時候。求最小生成樹的Prim演算法和Kruskal演算法都是漂亮的貪心演算法。 所以需要說明的是,貪心演算法可以與隨機化演算法一起使用,具體的例子就不再多舉了。(因為這一類演算法普及性不高,而且技術含量是非常高的,需要通過一些反例確定隨機的對象是什麼,隨機程度如何,但也是不能保證完全正確,只能是極大的幾率正確)
[編輯本段]附貪心演算法成功案例之一
馬踏棋盤的貪心演算法 123041-23 XX 【問題描述】 馬的遍歷問題。在8×8方格的棋盤上,從任意指定方格出發,為馬尋找一條走遍棋盤每一格並且只經過一次的一條最短路徑。 【初步設計】 首先這是一個搜索問題,運用深度優先搜索進行求解。演算法如下: 1、 輸入初始位置坐標x,y; 2、 步驟 c: 如果c> 64輸出一個解,返回上一步驟c-- (x,y) ← c 計算(x,y)的八個方位的子結點,選出那此可行的子結點 循環遍歷所有可行子結點,步驟c++重復2 顯然(2)是一個遞歸調用的過程,大致如下: void dfs(int x,int y,int count) { int i,tx,ty; if(count> N*N) { output_solution();//輸入一個解 return; }

Ⅳ 關於大數據的的相關技術

在大數據中,涉及到了很多技術,這些技術都是比較新穎的,比如說人工智慧、區塊鏈、圖靈測試等等,這些技術都是能夠幫助大數據解決很多問題。在這篇文章中我們就給大家介紹一下關於回歸分析、貪婪演算法、MapRece、數據挖掘的相關知識。
1.貪心演算法
貪心演算法是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,它所做出的是在某種意義上的局部最優解。貪心演算法不是對所有問題都能得到整體最優解,關鍵是貪心策略的選擇,選擇的貪心策略必須具備無後效性,即某個狀態以前的過程不會影響以後的狀態,只與當前狀態有關。貪心演算法的基本思路是從問題的某一個初始解出發一步一步地進行,根據某個優化測度,每一步都要確保能獲得局部最優解。由此可見,貪心演算法是十分實用的。
2.數據挖掘
數據挖掘是資料庫知識發現中的一個步驟。數據挖掘一般是指從大量的數據中通過演算法搜索隱藏於其中信息的過程。數據挖掘通常與計算機科學有關,並通過統計、在線分析處理、情報檢索、機器學習、專家系統和模式識別等諸多方法來實現上述目標。數據挖掘工作是一個十分重要的內容,在大數據和數據分析中廣泛實用。
3.回歸分析
回歸分析是確定兩種或兩種以上變數間相互依賴的定量關系的一種統計分析方法。運用十分廣泛,回歸分析按照涉及的變數的多少,分為一元回歸和多元回歸分析;按照因變數的多少,可分為簡單回歸分析和多重回歸分析;按照自變數和因變數之間的關系類型,可分為線性回歸分析和非線性回歸分析。如果在回歸分析中,只包括一個自變數和一個因變數,且二者的關系可用一條直線近似表示,這種回歸分析稱為一元線性回歸分析。
4.MapRece
MapRece是一種編程模型,用於大規模數據集的並行運算。概念"映射"和"歸約",是它們的主要思想,都是從函數式編程語言里借來的,還有從矢量編程語言里借來的特性。它極大地方便了編程人員在不會分布式並行編程的情況下,將自己的程序運行在分布式系統上。 當前的軟體實現是指定一個映射函數,用來把一組鍵值對映射成一組新的鍵值對,指定並發的歸約函數,用來保證所有映射的鍵值對中的每一個共享相同的鍵組。這些內容就是大數據分析工作中經常使用的演算法。
在這篇文章中我們介紹了關於回歸分析、貪婪演算法、MapRece、數據挖掘的相關知識,相信大家通過閱讀這篇文章以後對這些技術有了一定的理解。希望這篇文章能夠更好地幫助大家。

Ⅳ 貪心演算法 活動安排問題

這道題的貪心演算法比較容易理解,我就不多說明了,只是提到一下演算法思路1、建立數學模型描述問題。我在這里將時間理解成一條直線,上面有若干個點,可能是某些活動的起始時間點,或終止時間點。在具體一下,如果編程來實現的話,將時間抽象成鏈表數組,數組下標代表其實時間,該下標對應的鏈表代表在這個時間起始的活動都有哪些,具體參照程序注釋。2、問題分解。為了安排更多的活動,那麼每次選取佔用時間最少的活動就好。那麼從一開始就選取結束時間最早的,然後尋找在這個時間點上起始的活動,以此類推就可以找出貪心解。程序代碼:#include<stdio.h>
struct inode //自定義的結構體
{
int end; //表示結束時間
inode *next; //指向下一個節點的指針
};int main()
{
inode start[10001],*pt;
int a,b,i,num=0; //num負責計數,i控制循環,a,b輸入時候使用
for(i=0;i<10001;i++) //初始化
{
start[i].next=NULL;
}
while(scanf("%d %d",&a,&b)) //輸入並建立數據結構
{
if(a==0&&b==0) break;
pt=new inode; //創建新的節點,然後將該節點插入相應的位置
pt->end=b;
pt->next=start[a].next;
start[a].next=pt;
}
i=0;
while(i<10001) //進行貪心演算法,i表示當前時間
{
if(start[i].next==NULL)
{
i++; //該時間無活動開始
}
else
{
int temp=10001; //臨時變數,存儲該鏈表中最早的終止時間
for(pt=start[i].next;pt!=NULL;pt=pt->next)
{
if(pt->end<temp)
{
temp=pt->end;
}
}
i=temp; //將當前時間設置成前一子問題的終止時間
num++;
}
}
printf("%d\n",num); //列印結果
return 0;
}代碼並不一定是最快速的,但是可以求出貪心解,如果你做的是ACM編程題目,不保證能AC注釋我盡力寫了,希望對你有幫助。

Ⅵ 收集各類貪心演算法(C語言編程)經典題目

舉個例子,假如你買東西,老闆需要找給你99分錢,他有上面面值分別為25分,10分,5分,1分的硬幣(都是假如,不符合實際),他得找你3個25分,2個10分的,4個1分的才為最佳方案!
用貪心演算法編寫程序實現!
main()
{
int
i,a[5],b[4],c[4];
/*
define
the
type
of
the
money*/
a[1]=25;
a[2]=10;
a[3]=5;
a[4]=1;
printf("please
input
you
money
(fen):\n");
scanf("%d",&b[0]);
for
(i=1;i<=4;i++)
{
b[i]=b[i-1]%a[i];
/*take
n
25
off
and
money
left*/
c[i]=(b[i-1]-b[i])/a[i];
/*
n
*/
printf("%d
is
%d\n",a[i],c[i]);
}
getch();
}

Ⅶ Pascal貪心演算法,求解答!

這道題用貪心不大好吧
記得老師以前說過
這種題用DP
這道題是最簡單的01背包
我給你發個資料
那個,發不了啊,上傳失敗
你給我qq吧
P01: 01背包問題
題目
有N件物品和一個容量為V的背包。第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

基本思路
這是最基礎的背包問題,特點是:每種物品僅有一件,可以選擇放或不放。

用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量為v的背包可以獲得的最大價值。則其狀態轉移方程便是:f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}。

這個方程非常重要,基本上所有跟背包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:「將前i件物品放入容量為v的背包中」這個子問題,若只考慮第i件物品的策略(放或不放),那麼就可以轉化為一個只牽扯前i-1件物品的問題。如果不放第i件物品,那麼問題就轉化為「前i-1件物品放入容量為v的背包中」;如果放第i件物品,那麼問題就轉化為「前i-1件物品放入剩下的容量為v-c[i]的背包中」,此時能獲得的最大價值就是f [i-1][v-c[i]]再加上通過放入第i件物品獲得的價值w[i]。

注意f[i][v]有意義當且僅當存在一個前i件物品的子集,其費用總和為v。所以按照這個方程遞推完畢後,最終的答案並不一定是f[N] [V],而是f[N][0..V]的最大值。如果將狀態的定義中的「恰」字去掉,在轉移方程中就要再加入一項f[i][v-1],這樣就可以保證f[N] [V]就是最後的答案。至於為什麼這樣就可以,由你自己來體會了。

優化空間復雜度
以上方法的時間和空間復雜度均為O(N*V),其中時間復雜度基本已經不能再優化了,但空間復雜度卻可以優化到O(V)。

先考慮上面講的基本思路如何實現,肯定是有一個主循環i=1..N,每次算出來二維數組f[i][0..V]的所有值。那麼,如果只用一個數組f [0..V],能不能保證第i次循環結束後f[v]中表示的就是我們定義的狀態f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]兩個子問題遞推而來,能否保證在推f[i][v]時(也即在第i次主循環中推f[v]時)能夠得到f[i-1][v]和f[i-1][v-c[i]]的值呢?事實上,這要求在每次主循環中我們以v=V..0的順序推f[v],這樣才能保證推f[v]時f[v-c[i]]保存的是狀態f[i -1][v-c[i]]的值。偽代碼如下:

for i=1..N
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};

其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相當於我們的轉移方程f[i][v]=max{f[i-1][v],f[i- 1][v-c[i]]},因為現在的f[v-c[i]]就相當於原來的f[i-1][v-c[i]]。如果將v的循環順序從上面的逆序改成順序的話,那麼則成了f[i][v]由f[i][v-c[i]]推知,與本題意不符,但它卻是另一個重要的背包問題P02最簡捷的解決方案,故學習只用一維數組解01背包問題是十分必要的。

總結
01背包問題是最基本的背包問題,它包含了背包問題中設計狀態、方程的最基本思想,另外,別的類型的背包問題往往也可以轉換成01背包問題求解。故一定要仔細體會上面基本思路的得出方法,狀態轉移方程的意義,以及最後怎樣優化的空間復雜度。

P02: 完全背包問題
題目
有N種物品和一個容量為V的背包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

基本思路
這個問題非常類似於01背包問題,所不同的是每種物品有無限件。也就是從每種物品的角度考慮,與它相關的策略已並非取或不取兩種,而是有取0件、取1件、取2件……等很多種。如果仍然按照解01背包時的思路,令f[i][v]表示前i種物品恰放入一個容量為v的背包的最大權值。仍然可以按照每種物品不同的策略寫出狀態轉移方程,像這樣:f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k*c[i]<=v}。這跟01背包問題一樣有O(N*V)個狀態需要求解,但求解每個狀態的時間則不是常數了,求解狀態f[i][v]的時間是O(v/c[i]),總的復雜度是超過O(VN)的。

將01背包問題的基本思路加以改進,得到了這樣一個清晰的方法。這說明01背包問題的方程的確是很重要,可以推及其它類型的背包問題。但我們還是試圖改進這個復雜度。

一個簡單有效的優化
完全背包問題有一個很簡單有效的優化,是這樣的:若兩件物品i、j滿足c[i]<=c[j]且w[i]>=w[j],則將物品j去掉,不用考慮。這個優化的正確性顯然:任何情況下都可將價值小費用高得j換成物美價廉的i,得到至少不會更差的方案。對於隨機生成的數據,這個方法往往會大大減少物品的件數,從而加快速度。然而這個並不能改善最壞情況的復雜度,因為有可能特別設計的數據可以一件物品也去不掉。

轉化為01背包問題求解
既然01背包問題是最基本的背包問題,那麼我們可以考慮把完全背包問題轉化為01背包問題來解。最簡單的想法是,考慮到第i種物品最多選V/c [i]件,於是可以把第i種物品轉化為V/c[i]件費用及價值均不變的物品,然後求解這個01背包問題。這樣完全沒有改進基本思路的時間復雜度,但這畢竟給了我們將完全背包問題轉化為01背包問題的思路:將一種物品拆成多件物品。

更高效的轉化方法是:把第i種物品拆成費用為c[i]*2^k、價值為w[i]*2^k的若干件物品,其中k滿足c[i]*2^k<V。這是二進制的思想,因為不管最優策略選幾件第i種物品,總可以表示成若干個2^k件物品的和。這樣把每種物品拆成O(log(V/c[i]))件物品,是一個很大的改進。 但我們有更優的O(VN)的演算法。 * O(VN)的演算法 這個演算法使用一維數組,先看偽代碼: <pre class"example"> for i=1..N for v=0..Vf[v]=max{f[v],f[v-c[i]]+w[i]};

你會發現,這個偽代碼與P01的偽代碼只有v的循環次序不同而已。為什麼這樣一改就可行呢?首先想想為什麼P01中要按照v=V..0的逆序來循環。這是因為要保證第i次循環中的狀態f[i][v]是由狀態f[i-1][v-c[i]]遞推而來。換句話說,這正是為了保證每件物品只選一次,保證在考慮「選入第i件物品」這件策略時,依據的是一個絕無已經選入第i件物品的子結果f[i-1][v-c[i]]。而現在完全背包的特點恰是每種物品可選無限件,所以在考慮「加選一件第i種物品」這種策略時,卻正需要一個可能已選入第i種物品的子結果f[i][v-c[i]],所以就可以並且必須採用v= 0..V的順序循環。這就是這個簡單的程序為何成立的道理。

這個演算法也可以以另外的思路得出。例如,基本思路中的狀態轉移方程可以等價地變形成這種形式:f[i][v]=max{f[i-1][v],f[i][v-c[i]]+w[i]},將這個方程用一維數組實現,便得到了上面的偽代碼。

總結
完全背包問題也是一個相當基礎的背包問題,它有兩個狀態轉移方程,分別在「基本思路」以及「O(VN)的演算法「的小節中給出。希望你能夠對這兩個狀態轉移方程都仔細地體會,不僅記住,也要弄明白它們是怎麼得出來的,最好能夠自己想一種得到這些方程的方法。事實上,對每一道動態規劃題目都思考其方程的意義以及如何得來,是加深對動態規劃的理解、提高動態規劃功力的好方法。

P03: 多重背包問題
題目
有N種物品和一個容量為V的背包。第i種物品最多有n[i]件可用,每件費用是c[i],價值是w[i]。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

基本演算法
這題目和完全背包問題很類似。基本的方程只需將完全背包問題的方程略微一改即可,因為對於第i種物品有n[i]+1種策略:取0件,取1件……取n[i]件。令f[i][v]表示前i種物品恰放入一個容量為v的背包的最大權值,則:f[i][v]=max{f[i-1][v-k*c[i]]+k*w[i]|0<=k<=n[i]}。復雜度是O(V*∑n[i])。

轉化為01背包問題
另一種好想好寫的基本方法是轉化為01背包求解:把第i種物品換成n[i]件01背包中的物品,則得到了物品數為∑n[i]的01背包問題,直接求解,復雜度仍然是O(V*∑n[i])。

但是我們期望將它轉化為01背包問題之後能夠像完全背包一樣降低復雜度。仍然考慮二進制的思想,我們考慮把第i種物品換成若干件物品,使得原問題中第i種物品可取的每種策略——取0..n[i]件——均能等價於取若干件代換以後的物品。另外,取超過n[i]件的策略必不能出現。

方法是:將第i種物品分成若干件物品,其中每件物品有一個系數,這件物品的費用和價值均是原來的費用和價值乘以這個系數。使這些系數分別為 1,2,4,...,2^(k-1),n[i]-2^k+1,且k是滿足n[i]-2^k+1>0的最大整數。例如,如果n[i]為13,就將這種物品分成系數分別為1,2,4,6的四件物品。

分成的這幾件物品的系數和為n[i],表明不可能取多於n[i]件的第i種物品。另外這種方法也能保證對於0..n[i]間的每一個整數,均可以用若干個系數的和表示,這個證明可以分0..2^k-1和2^k..n[i]兩段來分別討論得出,並不難,希望你自己思考嘗試一下。

這樣就將第i種物品分成了O(log n[i])種物品,將原問題轉化為了復雜度為O(V*∑logn[i])的01背包問題,是很大的改進。

O(VN)的演算法
多重背包問題同樣有O(VN)的演算法。這個演算法基於基本演算法的狀態轉移方程,但應用單調隊列的方法使每個狀態的值可以以均攤O(1)的時間求解。由於用單調隊列優化的DP已超出了NOIP的范圍,故本文不再展開講解。我最初了解到這個方法是在樓天成的「男人八題」幻燈片上。

小結
這里我們看到了將一個演算法的復雜度由O(V*∑n[i])改進到O(V*∑log n[i])的過程,還知道了存在應用超出NOIP范圍的知識的O(VN)演算法。希望你特別注意「拆分物品」的思想和方法,自己證明一下它的正確性,並用盡量簡潔的程序來實現。

P04: 混合三種背包問題
問題
如果將P01、P02、P03混合起來。也就是說,有的物品只可以取一次(01背包),有的物品可以取無限次(完全背包),有的物品可以取的次數有一個上限(多重背包)。應該怎麼求解呢?

01背包與完全背包的混合
考慮到在P01和P02中最後給出的偽代碼只有一處不同,故如果只有兩類物品:一類物品只能取一次,另一類物品可以取無限次,那麼只需在對每個物品應用轉移方程時,根據物品的類別選用順序或逆序的循環即可,復雜度是O(VN)。偽代碼如下:

for i=1..N
if 第i件物品是01背包
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]};
else if 第i件物品是完全背包
for v=0..V
f[v]=max{f[v],f[v-c[i]]+w[i]};

再加上多重背包
如果再加上有的物品最多可以取有限次,那麼原則上也可以給出O(VN)的解法:遇到多重背包類型的物品用單調隊列解即可。但如果不考慮超過NOIP范圍的演算法的話,用P03中將每個這類物品分成O(log n[i])個01背包的物品的方法也已經很優了。

小結
有人說,困難的題目都是由簡單的題目疊加而來的。這句話是否公理暫且存之不論,但它在本講中已經得到了充分的體現。本來01背包、完全背包、多重背包都不是什麼難題,但將它們簡單地組合起來以後就得到了這樣一道一定能嚇倒不少人的題目。但只要基礎扎實,領會三種基本背包問題的思想,就可以做到把困難的題目拆分成簡單的題目來解決。
P05: 二維費用的背包問題
問題
二維費用的背包問題是指:對於每件物品,具有兩種不同的費用;選擇這件物品必須同時付出這兩種代價;對於每種代價都有一個可付出的最大值(背包容量)。問怎樣選擇物品可以得到最大的價值。設這兩種代價分別為代價1和代價2,第i件物品所需的兩種代價分別為a[i]和b[i]。兩種代價可付出的最大值(兩種背包容量)分別為V和U。物品的價值為w[i]。

演算法
費用加了一維,只需狀態也加一維即可。設f[i][v][u]表示前i件物品付出兩種代價分別為v和u時可獲得的最大價值。狀態轉移方程就是:f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}。如前述方法,可以只使用二維的數組:當每件物品只可以取一次時變數v和u採用順序的循環,當物品有如完全背包問題時採用逆序的循環。當物品有如多重背包問題時拆分物品。

物品總個數的限制
有時,「二維費用」的條件是以這樣一種隱含的方式給出的:最多隻能取M件物品。這事實上相當於每件物品多了一種「件數」的費用,每個物品的件數費用均為1,可以付出的最大件數費用為M。換句話說,設f[v][m]表示付出費用v、最多選m件時可得到的最大價值,則根據物品的類型(01、完全、多重)用不同的方法循環更新,最後在f[0..V][0..M]范圍內尋找答案。

另外,如果要求「恰取M件物品」,則在f[0..V][M]范圍內尋找答案。

小結
事實上,當發現由熟悉的動態規劃題目變形得來的題目時,在原來的狀態中加一緯以滿足新的限制是一種比較通用的方法。希望你能從本講中初步體會到這種方法。

P06: 分組的背包問題
問題
有N件物品和一個容量為V的背包。第i件物品的費用是c[i],價值是w[i]。這些物品被劃分為若干組,每組中的物品互相沖突,最多選一件。求解將哪些物品裝入背包可使這些物品的費用總和不超過背包容量,且價值總和最大。

演算法
這個問題變成了每組物品有若干種策略:是選擇本組的某一件,還是一件都不選。也就是說設f[k][v]表示前k組物品花費費用v能取得的最大權值,則有f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i屬於第k組}。

使用一維數組的偽代碼如下:

for 所有的組k
for 所有的i屬於組k
for v=V..0
f[v]=max{f[v],f[v-c[i]]+w[i]}

另外,顯然可以對每組中的物品應用P02中「一個簡單有效的優化」。

小結
分組的背包問題將彼此互斥的若干物品稱為一個組,這建立了一個很好的模型。不少背包問題的變形都可以轉化為分組的背包問題(例如P07),由分組的背包問題進一步可定義「泛化物品」的概念,十分有利於解題。

P07: 有依賴的背包問題
簡化的問題
這種背包問題的物品間存在某種「依賴」的關系。也就是說,i依賴於j,表示若選物品i,則必須選物品j。為了簡化起見,我們先設沒有某個物品既依賴於別的物品,又被別的物品所依賴;另外,沒有某件物品同時依賴多件物品。

演算法
這個問題由NOIP2006金明的預算方案一題擴展而來。遵從該題的提法,將不依賴於別的物品的物品稱為「主件」,依賴於某主件的物品稱為「附件」。由這個問題的簡化條件可知所有的物品由若干主件和依賴於每個主件的一個附件集合組成。

按照背包問題的一般思路,僅考慮一個主件和它的附件集合。可是,可用的策略非常多,包括:一個也不選,僅選擇主件,選擇主件後再選擇一個附件,選擇主件後再選擇兩個附件……無法用狀態轉移方程來表示如此多的策略。(事實上,設有n個附件,則策略有2^n+1個,為指數級。)

考慮到所有這些策略都是互斥的(也就是說,你只能選擇一種策略),所以一個主件和它的附件集合實際上對應於P06中的一個物品組,每個選擇了主件又選擇了若干個附件的策略對應於這個物品組中的一個物品,其費用和價值都是這個策略中的物品的值的和。但僅僅是這一步轉化並不能給出一個好的演算法,因為物品組中的物品還是像原問題的策略一樣多。

再考慮P06中的一句話: 可以對每組中的物品應用P02中「一個簡單有效的優化」。這提示我們,對於一個物品組中的物品,所有費用相同的物品只留一個價值最大的,不影響結果。所以,我們可以對主件i的「附件集合」先進行一次01背包,得到費用依次為0..V-c[i]所有這些值時相應的最大價值f'[0..V-c[i]]。那麼這個主件及它的附件集合相當於V-c[i]+1個物品的物品組,其中費用為c[i]+k的物品的價值為f'[k]+w[i]。也就是說原來指數級的策略中有很多策略都是冗餘的,通過一次01背包後,將主件i轉化為 V-c[i]+1個物品的物品組,就可以直接應用P06的演算法解決問題了。

更一般的問題
更一般的問題是:依賴關系以圖論中「森林」的形式給出(森林即多叉樹的集合),也就是說,主件的附件仍然可以具有自己的附件集合,限制只是每個物品最多隻依賴於一個物品(只有一個主件)且不出現循環依賴。

解決這個問題仍然可以用將每個主件及其附件集合轉化為物品組的方式。唯一不同的是,由於附件可能還有附件,就不能將每個附件都看作一個一般的01 背包中的物品了。若這個附件也有附件集合,則它必定要被先轉化為物品組,然後用分組的背包問題解出主件及其附件集合所對應的附件組中各個費用的附件所對應的價值。

事實上,這是一種樹形DP,其特點是每個父節點都需要對它的各個兒子的屬性進行一次DP以求得自己的相關屬性。這已經觸及到了「泛化物品」的思想。看完P08後,你會發現這個「依賴關系樹」每一個子樹都等價於一件泛化物品,求某節點為根的子樹對應的泛化物品相當於求其所有兒子的對應的泛化物品之和。

小結
NOIP2006的那道背包問題我做得很失敗,寫了上百行的代碼,卻一分未得。後來我通過思考發現通過引入「物品組」和「依賴」的概念可以加深對這題的理解,還可以解決它的推廣問題。用物品組的思想考慮那題中極其特殊的依賴關系:物品不能既作主件又作附件,每個主件最多有兩個附件,可以發現一個主件和它的兩個附件等價於一個由四個物品組成的物品組,這便揭示了問題的某種本質。

我想說:失敗不是什麼丟人的事情,從失敗中全無收獲才是。

P08: 泛化物品
定義
考慮這樣一種物品,它並沒有固定的費用和價值,而是它的價值隨著你分配給它的費用而變化。這就是泛化物品的概念。

更嚴格的定義之。在背包容量為V的背包問題中,泛化物品是一個定義域為0..V中的整數的函數h,當分配給它的費用為v時,能得到的價值就是h(v)。

這個定義有一點點抽象,另一種理解是一個泛化物品就是一個數組h[0..V],給它費用v,可得到價值h[V]。

一個費用為c價值為w的物品,如果它是01背包中的物品,那麼把它看成泛化物品,它就是除了h(c)=w其它函數值都為0的一個函數。如果它是完全背包中的物品,那麼它可以看成這樣一個函數,僅當v被c整除時有h(v)=v/c*w,其它函數值均為0。如果它是多重背包中重復次數最多為n的物品,那麼它對應的泛化物品的函數有h(v)=v/c*w僅當v被c整除且v/c<=n,其它情況函數值均為0。

一個物品組可以看作一個泛化物品h。對於一個0..V中的v,若物品組中不存在費用為v的的物品,則h(v)=0,否則h(v)為所有費用為v的物品的最大價值。P07中每個主件及其附件集合等價於一個物品組,自然也可看作一個泛化物品。

泛化物品的和
如果面對兩個泛化物品h和l,要用給定的費用從這兩個泛化物品中得到最大的價值,怎麼求呢?事實上,對於一個給定的費用v,只需枚舉將這個費用如何分配給兩個泛化物品就可以了。同樣的,對於0..V的每一個整數v,可以求得費用v分配到h和l中的最大價值f(v)。也即f(v)=max{h(k)+l(v-k)|0<=k<=v}。可以看到,f也是一個由泛化物品h和l決定的定義域為0..V的函數,也就是說,f是一個由泛化物品h和 l決定的泛化物品。

由此可以定義泛化物品的和:h、l都是泛化物品,若泛化物品f滿足f(v)=max{h(k)+l(v-k)|0<=k<=v},則稱f是h與l的和,即f=h+l。這個運算的時間復雜度是O(V^2)。

泛化物品的定義表明:在一個背包問題中,若將兩個泛化物品代以它們的和,不影響問題的答案。事實上,對於其中的物品都是泛化物品的背包問題,求它的答案的過程也就是求所有這些泛化物品之和的過程。設此和為s,則答案就是s[0..V]中的最大值。

背包問題的泛化物品
一個背包問題中,可能會給出很多條件,包括每種物品的費用、價值等屬性,物品之間的分組、依賴等關系等。但肯定能將問題對應於某個泛化物品。也就是說,給定了所有條件以後,就可以對每個非負整數v求得:若背包容量為v,將物品裝入背包可得到的最大價值是多少,這可以認為是定義在非負整數集上的一件泛化物品。這個泛化物品——或者說問題所對應的一個定義域為非負整數的函數——包含了關於問題本身的高度濃縮的信息。一般而言,求得這個泛化物品的一個子域(例如0..V)的值之後,就可以根據這個函數的取值得到背包問題的最終答案。

綜上所述,一般而言,求解背包問題,即求解這個問題所對應的一個函數,即該問題的泛化物品。而求解某個泛化物品的一種方法就是將它表示為若干泛化物品的和然後求之。

小結
本講可以說都是我自己的原創思想。具體來說,是我在學習函數式編程的 Scheme 語言時,用函數編程的眼光審視各類背包問題得出的理論。這一講真的很抽象,也許在「模型的抽象程度」這一方面已經超出了NOIP的要求,所以暫且看不懂也沒關系。相信隨著你的OI之路逐漸延伸,有一天你會理解的。

我想說:「思考」是一個OIer最重要的品質。簡單的問題,深入思考以後,也能發現更多。

P09: 背包問題問法的變化
以上涉及的各種背包問題都是要求在背包容量(費用)的限制下求可以取到的最大價值,但背包問題還有很多種靈活的問法,在這里值得提一下。但是我認為,只要深入理解了求背包問題最大價值的方法,即使問法變化了,也是不難想出演算法的。

例如,求解最多可以放多少件物品或者最多可以裝滿多少背包的空間。這都可以根據具體問題利用前面的方程求出所有狀態的值(f數組)之後得到。

還有,如果要求的是「總價值最小」「總件數最小」,只需簡單的將上面的狀態轉移方程中的max改成min即可。

Ⅷ 貪心演算法 部分背包問題

[背包問題]有一個背包,背包容量是M=150。有7個物品,物品可以分割成任意大小。
要求盡可能讓裝入背包中的物品總價值最大,但不能超過總容量。
物品 A B C D E F G
重量 35 30 60 50 40 10 25
價值 10 40 30 50 35 40 30
分析:
目標函數: ∑pi最大
約束條件是裝入的物品總重量不超過背包容量:∑wi<=M( M=150)
(1)根據貪心的策略,每次挑選價值最大的物品裝入背包,得到的結果是否最優?
(2)每次挑選所佔重量最小的物品裝入是否能得到最優解?
(3)每次選取單位重量價值最大的物品,成為解本題的策略。 ?
值得注意的是,貪心演算法並不是完全不可以使用,貪心策略一旦經過證明成立後,它就是一種高效的演算法。
貪心演算法還是很常見的演算法之一,這是由於它簡單易行,構造貪心策略不是很困難。
可惜的是,它需要證明後才能真正運用到題目的演算法中。
一般來說,貪心演算法的證明圍繞著:整個問題的最優解一定由在貪心策略中存在的子問題的最優解得來的。
對於例題中的3種貪心策略,都是無法成立(無法被證明)的,解釋如下:
(1)貪心策略:選取價值最大者。反例:
W=30
物品:A B C
重量:28 12 12
價值:30 20 20
根據策略,首先選取物品A,接下來就無法再選取了,可是,選取B、C則更好。
(2)貪心策略:選取重量最小。它的反例與第一種策略的反例差不多。
(3)貪心策略:選取單位重量價值最大的物品。反例:
W=30
物品:A B C
重量:28 20 10
價值:28 20 10
根據策略,三種物品單位重量價值一樣,程序無法依據現有策略作出判斷,如果選擇A,則答案錯誤。

閱讀全文

與貪心演算法可視化編程相關的資料

熱點內容
路由器多種加密方法 瀏覽:604
程序員阻止電腦自動彈出定位 瀏覽:166
如何做伺服器服務商 瀏覽:759
su剖切命令 瀏覽:726
devc編譯背景 瀏覽:211
學習單片機的意義 瀏覽:51
音頻演算法AEC 瀏覽:911
加密貨幣容易被盜 瀏覽:82
蘋果平板如何開啟隱私單個app 瀏覽:704
空調壓縮機一開就停止 瀏覽:528
如何下載虎牙app 瀏覽:847
日語年號的演算法 瀏覽:955
dev裡面的編譯日誌咋調出來 瀏覽:298
php函數引用返回 瀏覽:816
文件夾和文件夾的創建 瀏覽:259
香港加密貨幣牌照 瀏覽:838
程序員鼓勵自己的代碼 瀏覽:393
計算機網路原理pdf 瀏覽:752
吃雞國際體驗服為什麼伺服器繁忙 瀏覽:96
php中sleep 瀏覽:491