POJ - 2449 Remmarguts' Date(第k短路:spfa+A*)
題目鏈接:點(diǎn)擊查看
題目大意:給出一個有向圖,求第k短路
題目分析:偷學(xué)了一波A*,本來以為是多難的算法,其實(shí)就是bfs+優(yōu)先隊(duì)列的升級版,之前看的那些博客寫的都太深奧了,以至于看了一半啥都沒看懂然后就被嚇跑了,這里放一波zx學(xué)長PPT上的描述,我感覺簡潔精煉,幾句話就把這個算法的核心描述清楚了:
- A * 算法的實(shí)現(xiàn),A * =優(yōu)先隊(duì)列BFS+估價函數(shù)。
- 回顧優(yōu)先隊(duì)列bfs:優(yōu)先隊(duì)列BFS算法維護(hù)了一個優(yōu)先隊(duì)列,不斷從堆中取出當(dāng)前代價最小的狀態(tài)進(jìn)行擴(kuò)展。每個狀態(tài)第一次從堆中取出時,就得到了從初態(tài)到該狀態(tài)的最小代價。
- 局限:如果給定一個目標(biāo)狀態(tài),需要求出從初態(tài)到目標(biāo)狀態(tài)的最小代價,那么優(yōu)先隊(duì)列BFS這個優(yōu)先策略是不完善的。一個狀態(tài)當(dāng)前代價小,只能說明從起始狀態(tài)到該狀態(tài)代價小,而在未來的搜索中從該狀態(tài)到目標(biāo)狀態(tài)的花費(fèi)可能會很大。導(dǎo)致有一部分很晚才能得到擴(kuò)展。
- 為了提高搜索效率,我們很自然的想到,可以對未來可能產(chǎn)生的代價進(jìn)行預(yù)估。
- 詳細(xì)的講:我們設(shè)計(jì)一個估價函數(shù),以任意狀態(tài)為輸入,計(jì)算出從該狀態(tài)到目標(biāo)狀態(tài)所需代價的估計(jì)值。在搜索中,仍然維護(hù)一個堆,不斷從堆中取出 當(dāng)前代價+未來估價 最小的狀態(tài)進(jìn)行擴(kuò)展
- 為了保證第一次從堆中取出目標(biāo)狀態(tài)時得到的就是最優(yōu)解,我們設(shè)計(jì)的估價函數(shù)需要滿足一個基本準(zhǔn)則:估價函數(shù)的估值不能大于未來實(shí)際代價,估價比實(shí)際代價更優(yōu)。
- 這種帶有估價函數(shù)的優(yōu)先隊(duì)列BFS就稱為A * 算法。只要保證對于任意狀態(tài)state,都有f(state)≤g(state),A * 算法就一定能在目標(biāo)狀態(tài)第一次從堆中被取出時得到最優(yōu)解,并且在搜索過程中每個狀態(tài)只需要被擴(kuò)展一次(之后再被取出就可以直接忽略)。估價f(state)越準(zhǔn)確,越接近g(state),A * 算法的效率就越高。如果估價始終為0,就等價于普通優(yōu)先隊(duì)列BFS。
- A * 算法提高搜索效率的關(guān)鍵,就在于能否設(shè)計(jì)出一個優(yōu)秀的估價函數(shù)。估價函數(shù)在滿足上述設(shè)計(jì)準(zhǔn)則的前提下,含應(yīng)該盡可能反映未來實(shí)際代價的變化趨勢和相對大小關(guān)系,這樣搜索才會較快的逼近最優(yōu)解
- 估價函數(shù)的設(shè)計(jì)準(zhǔn)則:
- 估值f(state)≤未來實(shí)際代價g(state)
那么再回到這個題目上面,我們只需要設(shè)計(jì)出估價函數(shù)即可,這個題目的權(quán)值是距離,當(dāng)我們到達(dá)一個點(diǎn)后,利用bfs的狀態(tài)轉(zhuǎn)移可以很容易的知道從起點(diǎn)到當(dāng)前點(diǎn)的距離,那怎么知道當(dāng)前點(diǎn)到終點(diǎn)的距離呢?我們可以一開始從終點(diǎn)跑一遍迪杰斯特拉或者spfa,這樣就能輕松表達(dá)出估價函數(shù)了,有了估價函數(shù)后,我們在優(yōu)先隊(duì)列+bfs的基礎(chǔ)上,更改排序函數(shù)的機(jī)制為估價函數(shù),然后給我們的bfs函數(shù)改個名字,就變成A*算法了
有一個坑點(diǎn)需要注意一下,當(dāng)終點(diǎn)和起點(diǎn)重合的時候,我們需要讓k++,以避免出現(xiàn)讓起點(diǎn)直接到達(dá)終點(diǎn)的現(xiàn)象發(fā)生
代碼,模板題:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e3+100;int d[N];bool vis[N];struct Node {int to,w;Node(int TO,int W){to=TO;w=W;}Node(){}bool operator<(const Node& a)const{return w+d[to]>a.w+d[a.to];} };vector<Node>node1[N],node2[N];//1:正向邊 2:反向邊 void spfa(int x) {memset(vis,false,sizeof(vis));memset(d,inf,sizeof(d));queue<int>q;q.push(x);d[x]=0;vis[x]=true;while(!q.empty()){int from=q.front();q.pop();vis[from]=false;for(int i=0;i<node2[from].size();i++){int to=node2[from][i].to;int w=node2[from][i].w;if(d[to]>d[from]+w){d[to]=d[from]+w;if(!vis[to]){vis[to]=true;q.push(to);}}}} }int A_star(int start,int end,int k) {priority_queue<Node>q;q.push(Node(start,0));while(!q.empty()){Node cur=q.top();q.pop();if(cur.to==end){k--;if(!k)return cur.w;}for(int i=0;i<node1[cur.to].size();i++){int to=node1[cur.to][i].to;int w=node1[cur.to][i].w;q.push(Node(to,cur.w+w));}}return -1; }int main() { // freopen("input.txt","r",stdin);int n,m;while(scanf("%d%d",&n,&m)!=EOF){for(int i=1;i<=n;i++){node1[i].clear();node2[i].clear();}while(m--){int u,v,w;scanf("%d%d%d",&u,&v,&w);node1[u].push_back(Node(v,w));node2[v].push_back(Node(u,w));}int s,e,k;scanf("%d%d%d",&s,&e,&k);spfa(e);if(d[s]==inf){printf("-1\n");continue;}if(s==e)//特判一下,坑 k++;printf("%d\n",A_star(s,e,k));}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的POJ - 2449 Remmarguts' Date(第k短路:spfa+A*)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDU - 3085 Nightmare
- 下一篇: qduoj - 小Z的集训队考验(拓扑排