當前位置:首頁 » 操作系統 » 單源最短路徑貪心演算法

單源最短路徑貪心演算法

發布時間: 2022-05-15 10:07:56

㈠ 數學最短路徑問題最方便的解法是什麼

用於解決最短路徑問題的演算法被稱做「最短路徑演算法」 ,有時被簡稱作「路徑演算法」 。最常用 的路徑演算法有: Dijkstra 演算法、 A*演算法、 SPFA 演算法、 Bellman-Ford 演算法和 Floyd-Warshall 演算法, 本文主要介紹其中的三種。 最短路徑問題是圖論研究中的一個經典演算法問題,旨在尋找圖(由結點和路徑組成的)中兩 結點之間的最短路徑。 演算法具體的形式包括: 確定起點的最短路徑問題:即已知起始結點,求最短路徑的問題。 確定終點的最短路徑問題:與確定起點的問題相反,該問題是已知終結結點,求最短路徑的 問題。 在無向圖中該問題與確定起點的問題完全等同, 在有向圖中該問題等同於把所有路徑 方向反轉的確定起點的問題。 確定起點終點的最短路徑問題:即已知起點和終點,求兩結點之間的最短路徑。 全局最短路徑問題:求圖中所有的最短路徑。 Floyd 求多源、無負權邊的最短路。用矩陣記錄圖。時效性較差,時間復雜度 O(V^3)。 Floyd-Warshall 演算法(Floyd-Warshall algorithm)是解決任意兩點間的最短路徑的一種演算法, 可以正確處理有向圖或負權的最短路徑問題。 Floyd-Warshall 演算法的時間復雜度為 O(N^3),空間復雜度為 O(N^2)。 Floyd-Warshall 的原理是動態規劃: 設 Di,j,k 為從 i 到 j 的只以(1..k)集合中的節點為中間節點的最短路徑的長度。 若最短路徑經過點 k,則 Di,j,k = Di,k,k-1 + Dk,j,k-1; 若最短路徑不經過點 k,則 Di,j,k = Di,j,k-1。 因此,Di,j,k = min(Di,k,k-1 + Dk,j,k-1 , Di,j,k-1)。 在實際演算法中,為了節約空間,可以直接在原來空間上進行迭代,這樣空間可降至二維。 Floyd-Warshall 演算法的描述如下: 1.for k ← 1 to n do 2.for i ← 1 to n do 3.for j ← 1 to n do 4.if (Di,k + Dk,j<Di,j) then 5.Di,j ← Di,k + Dk,j; 其中 Di,j 表示由點 i 到點 j 的代價,當 Di,j 為∞表示兩點之間沒有任何連接。 Dijkstra 求單源、無負權的最短路。時效性較好,時間復雜度為 O(V*V+E) 。 源點可達的話,O(V*lgV+E*lgV)=>O(E*lgV) 。 當是稀疏圖的情況時,此時 E=V*V/lgV,所以演算法的時間復雜度可為 O(V^2) 。若是斐波那 契堆作優先隊列的話,演算法時間復雜度,則為 O(V*lgV + E) 。 Bellman-Ford 求單源最短路,可以判斷有無負權迴路(若有,則不存在最短路) ,時效性較好,時間復雜 度 O(VE) 。 Bellman-Ford 演算法是求解單源最短路徑問題的一種演算法。 單源點的最短路徑問題是指:給定一個加權有向圖 G 和源點 s,對於圖 G 中的任意一點 v, 求從 s 到 v 的最短路徑。 與 Dijkstra 演算法不同的是,在 Bellman-Ford 演算法中,邊的權值可以為負數。設想從我們可以 從圖中找到一個環路(即從 v 出發,經過若干個點之後又回到 v)且這個環路中所有邊的權 值之和為負。那麼通過這個環路,環路中任意兩點的最短路徑就可以無窮小下去。如果不處 理這個負環路,程序就會永遠運行下去。而 Bellman-Ford 演算法具有分辨這種負環路的能力。 SPFA是 Bellman-Ford 的隊列優化,時效性相對好,時間復雜度 O(kE)(k<<V) 。 。 與 Bellman-ford 演算法類似, SPFA 演算法採用一系列的鬆弛操作以得到從某一個節點出發到達圖 中其它所有節點的最短路徑。所不同的是,SPFA 演算法通過維護一個隊列,使得一個節點的 當前最短路徑被更新之後沒有必要立刻去更新其他的節點, 從而大大減少了重復的操作次數。 SPFA 演算法可以用於存在負數邊權的圖,這與 dijkstra 演算法是不同的。 與 Dijkstra 演算法與 Bellman-ford 演算法都不同,SPFA 的演算法時間效率是不穩定的,即它對於不 同的圖所需要的時間有很大的差別。 在最好情形下,每一個節點都只入隊一次,則演算法實際上變為廣度優先遍歷,其時間復雜度 僅為 O(E)。另一方面,存在這樣的例子,使得每一個節點都被入隊(V-1)次,此時演算法退化為 Bellman-ford 演算法,其時間復雜度為 O(VE)。 SPFA 演算法在負邊權圖上可以完全取代 Bellman-ford 演算法, 另外在稀疏圖中也表現良好。 但是 在非負邊權圖中,為了避免最壞情況的出現,通常使用效率更加穩定的 Dijkstra 演算法,以及 它的使用堆優化的版本。通常的 SPFA 演算法在一類網格圖中的表現不盡如人意。

㈡ 各邊的權值為0~N-1之間的整數,N為一非負整數。修改Dijkstra演算法使其能在O(Nn

摘要 您好,,給定帶權有向圖G =(V,E),其中每條邊的權是非負實數。另外,還給定V中的一個頂點,稱為源。現在要計算從源到所有其他各頂點的最短路長度。這里路的長度是指路上各邊權之和。這個問題通常稱為單源最短路徑問題。

㈢ 貪婪啟發式和貪婪演算法的區別是什麼

馬踏棋盤的問題很早就有人提出,且早在1823年,J.C.Warnsdorff就提出了一個有名的演算法。在每個結點對其子結點進行選取時,優先選擇『出口』最小的進行搜索,『出口』的意思是在這些子結點中它們的可行子結點的個數,也就是『孫子』結點越少的越優先跳,為什麼要這樣選取,這是一種局部調整最優的做法,如果優先選擇出口多的子結點,那出口少的子結點就會越來越多,很可能出現『死』結點(顧名思義就是沒有出口又沒有跳過的結點),這樣對下面的搜索純粹是徒勞,這樣會浪費很多無用的時間,反過來如果每次都優先選擇出口少的結點跳,那出口少的結點就會越來越少,這樣跳成功的機會就更大一些。這種演算法稱為為貪心演算法,也叫貪婪演算法或啟發式演算法,它對整個求解過程的局部做最優調整,它只適用於求較優解或者部分解,而不能求最優解。這樣的調整方法叫貪心策略,至於什麼問題需要什麼樣的貪心策略是不確定的,具體問題具體分析。實驗可以證明馬遍歷問題在運用到了上面的貪心策略之後求解速率有非常明顯的提高,如果只要求出一個解甚至不用回溯就可以完成,因為在這個演算法提出的時候世界上還沒有計算機,這種方法完全可以用手工求出解來,其效率可想而知。
貪心演算法當然也有正確的時候。求最小生成樹的Prim演算法和Kruskal演算法都是漂亮的貪心演算法。
貪心法的應用演算法有Dijkstra的單源最短路徑和Chvatal的貪心集合覆蓋啟發式
所以需要說明的是,貪心演算法可以與隨機化演算法一起使用,具體的例子就不再多舉了。其實很多的智能演算法(也叫啟發式演算法),本質上就是貪心演算法和隨機化演算法結合——這樣的演算法結果雖然也是局部最優解,但是比單純的貪心演算法更靠近了最優解。例如遺傳演算法,模擬退火演算法。

㈣ 單源最短路徑可以用貪心演算法得到最優解嗎

可以。對於權值大於等於零的有相或無相圖,可以使用基於貪心思想的Dijkstra演算法求解單源最短路徑問題。

㈤ dijakstra演算法和分支限演算法在解決單源最短路徑問題的異同

記dijakstra演算法為D演算法
D演算法為貪心演算法,每一步的選擇為當前步的最優,復雜度為O(n*n) (又叫爬山法)
分支限界演算法,每一步的擴散為當前耗散度的最優,復雜度為(沒算)

都是A演算法的極端情況
(說錯了哈,下面我的文字中的的分支限界演算法實際上是在說動態規劃法,我查了一下書,動態規劃法是對分支限界法的改進,分支限界法不屬於A演算法(啟發式搜索演算法),但是這時用動態規劃法和D演算法比較也是有可比性的,而直接用分支限界演算法和D演算法比較也是可以的)
關鍵詞:耗散度 評估函數
即:對當前優先搜索方向的判斷標准為,有可能的最優解
而最優解可以用一個評估函數來做,即已經有的耗散度加上以後有可能的耗度
A演算法就是把兩個耗散度加在一起,作為當前狀態的搜索搜索方向;
但是對以後的耗散度的評估是麻煩的,D演算法就是把當前有的路的最短的作為,以後耗散度的評估.
分支限界演算法就是只以以前的耗散度為評估函數

你給的兩個演算法當然是A演算法的特例
你還可以參考一下 A*演算法 修正的A*演算法,相信對你的編程水平有幫助

參考:
隊列式分支限界法的搜索解空間樹的方式類似於解空間樹的寬度優先搜索,不同的是隊列式分支限界法不搜索以不可行結點(已經被判定不能導致可行解或不能導致最優解的結點)為根的子樹。按照規則,這樣的結點不被列入活結點表。

優先隊列式分支限界法的搜索方式是根據活結點的優先順序確定下一個擴展結點。結點的優先順序常用一個與該結點有關的數值p來表示。最大優先隊列規定p值較大的結點點的優先順序較高。在演算法實現時通常用一個最大堆來實現最大優先隊列,體現最大效益優先的原則。類似地,最小優先隊列規定p值較小的結點的優先順序較高。在演算法實現時,常用一個最小堆來實現,體現最小優先的原則。採用優先隊列式分支定界演算法解決具體問題時,應根據問題的特點選用最大優先或最小優先隊列,確定各個結點點的p值。

㈥ 關於Dijkstra演算法

樓上正解,你找個圖自己用此演算法實踐一下就知道了,從A點出發,發現離A最近的點是B點,那麼我們就已經認為A到B的最短距離就是AB了,如果有負數,就指不定冒出個C點,AC+CB<AB;或者冒出個DE為很大的負值,AC+CD+DE+EF+FB<AB;等等諸如此類的情況。
簡單說來,你駕車從家出發到某地沿某條路只需經過一個收費站,但是遠在外省某地有個站不但不收你的費,你去了還會給你個千八百萬的歡迎光臨費,你能說你直接沿著這條路去某地是最省費用的?不計時間成本,繞到外省那個給你錢的地方,再繞回到你的目的地,還能賺錢呢。
而且一般權值為負的圖研究也比較少。有些帶負權的圖,某些點間還沒有最小距離呢。中間出個帶某條負權很大的邊的環圈,繞此一圈所經過的距離反而減少了,那就一直在此圈上繞啊繞啊繞到負的足夠大溢出為止。
當然考慮各種自己隨便假設出來的變種問題也是很有趣的。比如說邊帶有多個權值對應多次經過改變的消費,上面的問題有可能變成有解的。話說那個站會後悔,第二次經過它會收回100萬,第三次經過收回250萬,這樣的話你只需要經過一次就夠了,問題也是有解的。再比如說對於多權重圖,從A點出發經過B點到達C點的最短路線,就不是簡單的AB最短路線+BC最短路線了,說不定兩者有重合邊,第二次經過來個天價就傻眼了。其實這種圖貌似應該可以轉化成單權重圖的,我直覺估計啊,剛隨便想出這個問題,還沒去思考這個問題的解^_^

㈦ 求單源最短路徑的程序設計(要貪心演算法的喲)

迪傑斯特拉演算法

// dijsktra.cpp : 定義控制台應用程序的入口點。
//

#include "stdafx.h"
#define N 12
#include <iostream>
using namespace std;

const static int soure[N][N] =
{
/*
*這里填鄰接矩陣
*/
};

int min(int arr[N],bool bj[])
{
int tmp = 999;
int temp = 0;
for(int i=0; i<N; i++)
{
if((arr[i]<tmp)&&(bj[i]==true))
{
tmp = arr[i];
temp = i;
}
}
return temp;
}

class dijsktra
{
private:
int dist[N][N];
int path[N][N];
int final[N][N];
bool flag[N];
public:
void Doing()
{
for(int i=0; i<N; i++)
{
int temp = 0;
for(int j=0; j<N; j++)
{
flag[j] = true;
}

for(int j=0; j<N; j++)
{
dist[i][j] = soure[i][j];
path[i][j] = i;
}
flag[i] = false;
temp = min(dist[i],flag);
flag[temp] = false;

for(int j=1; j<N; j++)
{
for(int k=0; k<N; k++)
{
if((flag[k] == true)&&((soure[temp][k]+dist[i][temp])<dist[i][k]))
{
dist[i][k] = soure[temp][k]+dist[i][temp];
path[i][k] = temp;
}
}
temp = min(dist[i],flag);
flag[temp] = false;
}
}
}

void print()
{
for(int i=0; i<N; i++)
{
for(int j=0; j<N; j++)
{
cout<<dist[i][j]<<","<<path[i][j]<<" ";
}
cout<<endl;
}
}

void l_print()
{
int i,j;
cout<<"請輸入i,j的值:";
cin>>i>>j;
cout<<"最短路徑長度為:"<<dist[i][j]<<endl;
cout<<"路徑為";
int temp = j;
while(path[i][temp]!=i)
{
cout<<temp<<"<-";
temp = path[i][temp];
}
cout<<temp<<"<-";
cout<<i<<endl;
}

};

int _tmain(int argc, _TCHAR* argv[])
{
dijsktra test;
test.Doing();
test.print();
test.l_print();
system("pause");
return 0;
}

㈧ 用動態規劃解決矩陣鏈乘法問題時,最優子結構問題是什麼

1、兩種重要演算法思想: 動態規劃,貪心演算法
2、動態規劃:
基本原理:動態規劃英文名dynamic programming。其中pogramming指的是表格法,而非編寫計算機程序。因此,可以初步得出動態規劃的基本思想:將一個具有最優子結構性質的問題分成若干個子問題,在求解過程中,記錄下子問題的結果,存儲在一個表格中,使得公共的子問題只需要計算一次。書中給出的基本原理:動態規劃將問題分成若干個相互重疊的子問題,遞歸的求解子問題,保存子問題的解,再將它們的解組合起來,求出原問題的解。
從基本原理中可以看出動態規劃需要滿足兩個條件,最優子結構和子問題重疊。
最優子結構:書中定義:問題的最優解由相關子問題的最優解組合而成,一個問題的最優解包含其子問題的最優解。典型的有背包問題和鋼條切割我問題。所謂子問題就是一中組合,將一個問題分成許多子問題的集合。某個子問題轉化為問題時,所需要的代價是固定的。
一般這類問題的解題過程:(自己總結)
畫出子問題圖(類似於逆拓撲排序的圖,子問題必須在問題前面完成)
用數學表達式構建出問題的最優解和子問題最優解之間的代數表達式
通常採用自底向上的方法進行遞歸地求解問題的解,自底下上的含義是從最小的子問題求起。
保存每一步求出的子問題的最優解
利用計算出的信息構造一個最優解
3、貪心演算法:
基本原理:從初始狀態出發,每步都經過貪心選擇,最終得到問題的最優解。
含義: 將每一步都看成是當前最佳的選擇,做到局部最優化,直到無法選擇為止。寄希望於局部最優的選擇能夠導致全局最優解。
兩個實例:最小生成樹演算法和單源最短路徑演算法,以及集合覆蓋問題的貪心啟發式演算法。
prim演算法:將集合A看成是一棵樹,每次選擇剩餘的節點中與這棵樹形成的邊的權值最小的點加入到集合A中形成新的樹,循壞調用該過程,知道所有的點都已經放入到集合A中。初始時隨機選擇一個節點放入到集合A中。
kruskal演算法:在所有連接森林中兩顆不同樹的邊裡面,找到權重最小的邊(u,v),並將其加入到集合A中,循環調用該過程,直到所有的點已經放入到集合A中
貪心選擇:當進行選擇時,我們直接作在當前問題看來是最優的選擇,而不必考慮子問題的解。這與動態規劃不同,動態規劃當前問題依賴於較小的子問題。而貪心演算法中做當前問題最優選擇,這樣每步之後只需要做一個子問題的解。
也必須滿足最優子結構的性質,即一個問題的最優解包含其子問題的最優解。
那麼,如何區分什麼時候使用動態規劃,什麼時候使用貪心演算法呢?
典型的兩個問題,0-1背包和分數背包。兩者都具有最優子結構性質,但是貪心演算法只能用來求分數背包的問題,而不能用來求0-1背包的問題。即只有分數背包具有貪心選擇性。
我得總結(不一定對):具有貪心選擇性的一類問題是:每次做選擇時只有性能不同,而代價是一樣的。那麼這樣每次的選擇都是最好的,最終會得到最好的結果。
哈夫曼編碼也使用貪心選擇演算法。每次選擇待編碼的字元都選擇在剩餘的字元中出現次數最多的

㈨ 演算法分析與設計這門課程第四章貪心演算法的知識點有哪些

演算法分析與設計這門課第四章貪心演算法的知識點包含章節導引,第一節活動安排問題,第二節貪心演算法基本要素,第三節最優裝載,第四節單源最短路徑,第五節多機調度問題,課後練習,。

㈩ 貪心演算法中的matlab演算法怎麼做

1.數論演算法
求兩數的最大公約數
function gcd(a,b:integer):integer;
begin
if b=0 then gcd:=a
else gcd:=gcd (b,a mod b);
end ;

求兩數的最小公倍數
function lcm(a,b:integer):integer;
begin
if a< b then swap(a,b);
lcm:=a;
while lcm mod b >0 do inc(lcm,a);
end;

素數的求法
A.小范圍內判斷一個數是否為質數:
function prime (n: integer): Boolean;
var I: integer;
begin
for I:=2 to trunc(sqrt(n)) do
if n mod I=0 then
begin
prime:=false; exit;
end;
prime:=true;
end;

B.判斷longint范圍內的數是否為素數(包含求50000以內的素數表):
procere getprime;
var
i,j:longint;
p:array[1..50000] of boolean;
begin
fillchar(p,sizeof(p),true);
p[1]:=false;
i:=2;
while i< 50000 do
begin
if p then
begin
j:=i*2;
while j< 50000 do
begin
p[j]:=false;
inc(j,i);
end;
end;
inc(i);
end;
l:=0;
for i:=1 to 50000 do
if p then
begin
inc(l);
pr[l]:=i;
end;
end;{getprime}
function prime(x:longint):integer;
var i:integer;
begin
prime:=false;
for i:=1 to l do
if pr >=x then break
else if x mod pr=0 then exit;
prime:=true;
end;{prime}

2.

3.

4.求最小生成樹
A.Prim演算法:
procere prim(v0:integer);
var
lowcost,closest:array[1..maxn] of integer;
i,j,k,min:integer;
begin
for i:=1 to n do
begin
lowcost:=cost[v0,i];
closest:=v0;
end;
for i:=1 to n-1 do
begin
{尋找離生成樹最近的未加入頂點k}
min:=maxlongint;
for j:=1 to n do
if (lowcost[j]< min) and (lowcost[j]< >0) then
begin
min:=lowcost[j];
k:=j;
end;
lowcost[k]:=0; {將頂點k加入生成樹}
{生成樹中增加一條新的邊k到closest[k]}
{修正各點的lowcost和closest值}
for j:=1 to n do
if cost[k,j]< lwocost[j] then
begin
lowcost[j]:=cost[k,j];
closest[j]:=k;
end;
end;
end;{prim}
B.Kruskal演算法:(貪心)
按權值遞增順序刪去圖中的邊,若不形成迴路則將此邊加入最小生成樹。
function find(v:integer):integer; {返回頂點v所在的集合}
var i:integer;
begin
i:=1;
while (i< =n) and (not v in vset) do inc(i);
if i< =n then find:=i
else find:=0;
end;
procere kruskal;
var
tot,i,j:integer;
begin
for i:=1 to n do vset:=;{初始化定義n個集合,第I個集合包含一個元素I}
p:=n-1; q:=1; tot:=0; {p為尚待加入的邊數,q為邊集指針}
sort;
{對所有邊按權值遞增排序,存於e[I]中,e[I].v1與e[I].v2為邊I所連接的兩個頂點的序號,e[I].len為第I條邊的長度}
while p >0 do
begin
i:=find(e[q].v1);j:=find(e[q].v2);
if i< >j then
begin
inc(tot,e[q].len);
vset:=vset+vset[j];vset[j]:=[];
dec(p);
end;
inc(q);
end;
writeln(tot);
end;

5.最短路徑
A.標號法求解單源點最短路徑:
var
a:array[1..maxn,1..maxn] of integer;
b:array[1..maxn] of integer; {b指頂點i到源點的最短路徑}
mark:array[1..maxn] of boolean;

procere bhf;
var
best,best_j:integer;
begin
fillchar(mark,sizeof(mark),false);
mark[1]:=true; b[1]:=0;{1為源點}
repeat
best:=0;
for i:=1 to n do
If mark then {對每一個已計算出最短路徑的點}
for j:=1 to n do
if (not mark[j]) and (a[i,j] >0) then
if (best=0) or (b+a[i,j]< best) then
begin
best:=b+a[i,j]; best_j:=j;
end;
if best >0 then
begin
b[best_j]:=best;mark[best_j]:=true;
end;
until best=0;
end;{bhf}

B.Floyed演算法求解所有頂點對之間的最短路徑:
procere floyed;
begin
for I:=1 to n do
for j:=1 to n do
if a[I,j] >0 then p[I,j]:=I else p[I,j]:=0;
{p[I,j]表示I到j的最短路徑上j的前驅結點}
for k:=1 to n do {枚舉中間結點}
for i:=1 to n do
for j:=1 to n do
if a[i,k]+a[j,k]< a[i,j] then
begin
a[i,j]:=a[i,k]+a[k,j];
p[I,j]:=p[k,j];
end;
end;
C. Dijkstra 演算法:
類似標號法,本質為貪心演算法。
var
a:array[1..maxn,1..maxn] of integer;
b,pre:array[1..maxn] of integer; {pre指最短路徑上I的前驅結點}
mark:array[1..maxn] of boolean;
procere dijkstra(v0:integer);
begin
fillchar(mark,sizeof(mark),false);
for i:=1 to n do
begin
d:=a[v0,i];
if d< >0 then pre:=v0 else pre:=0;
end;
mark[v0]:=true;
repeat {每循環一次加入一個離1集合最近的結點並調整其他結點的參數}
min:=maxint; u:=0; {u記錄離1集合最近的結點}
for i:=1 to n do
if (not mark) and (d< min) then
begin
u:=i; min:=d;
end;
if u< >0 then
begin
mark:=true;
for i:=1 to n do
if (not mark) and (a[u,i]+d< d) then
begin
d:=a[u,i]+d;
pre:=u;
end;
end;
until u=0;
end;
D.計算圖的傳遞閉包
Procere Longlink;
Var
T:array[1..maxn,1..maxn] of boolean;
Begin
Fillchar(t,sizeof(t),false);
For k:=1 to n do
For I:=1 to n do
For j:=1 to n do
T[I,j]:=t[I,j] or (t[I,k] and t[k,j]);
End;

熱點內容
mysql安裝圖解linux 發布:2024-04-25 05:18:59 瀏覽:605
有哪些配置中心的框架 發布:2024-04-25 05:11:37 瀏覽:829
進程的調度演算法代碼 發布:2024-04-25 04:25:20 瀏覽:588
maven編譯scala 發布:2024-04-25 04:25:11 瀏覽:110
手機存儲空間里的其他 發布:2024-04-25 04:10:42 瀏覽:27
文件改文件夾 發布:2024-04-25 04:03:00 瀏覽:563
50次方編程 發布:2024-04-25 04:02:59 瀏覽:58
編程首行 發布:2024-04-25 03:56:43 瀏覽:382
蘋果手機輸入密碼為什麼是灰色的 發布:2024-04-25 03:43:27 瀏覽:641
java鄭州 發布:2024-04-25 03:24:45 瀏覽:100