1. 程式人生 > 實用技巧 >A*(A star)搜尋總結

A*(A star)搜尋總結

定義

先複製一則定義

A*演算法在人工智慧中是一種典型的啟發式搜尋演算法
啟發中的估價是用估價函式表示的:
h(n)=f(n)+g(n)
其中f(n)是節點n的估價函式
g(n)表示實際狀態空間中從初始節點到n節點的實際代價
h(n)是從n到目標節點最佳路徑的估計代價。
另外定義h'(n)為n到目標節點最佳路徑的實際值。
如果h'(n)≥h(n)則如果存在從初始狀態走到目標狀態的最小代價的解
那麼用該估價函式搜尋的演算法就叫A*演算法。

有點繁瑣,但也看得過去

通俗來講

的核心在於上面所講到的估價函式
他是幹什麼用的呢
就是我們在搜尋的過程中,保證更優的先搜用的
還是有些繁瑣對不對,嗯,我也不大會講啊(沒事我會加油


嘿,認真看下面,我可認真了的啊。。。

如果一個題目要求我們求前K個代價最小的解(只是一個典型,不是所有題目都這樣)
假設我們現在有一個狀態在
已經要記錄到答案裡面的代價是(我喜歡用這個)
我們發現如果爆搜的話狀態會是亂的對不對,肯定會使搜尋搜到太多
而如果直接把狀態按照排序的話不能保證答案就會正確(當然,不然就去貪心去)
所以我們引進一個估價函式狀態
當然要求一般是可以預處理出一個狀態到答案狀態的最優解
回到前面講到的當前狀態
如果我們把與並列的所有狀態按排序呢?
既不影響答案的正確性,又可以減少壞狀態的轉移
(因為題目要求是K個最優狀態,而這樣待決策狀態會有序且跑完K個就可以結束,所以會變快)

好吧,還有點蒙對不對,那我們看例題

例題

[洛谷P2901 USACO08MAR]牛慢跑Cow Jogging
好像其他很多都有,但是是許可權。。。

題目簡述

要求我們求出從起點n到終點1的最短K條路徑的長度
(只能從編號大的點往編號小的點走&邊有邊權)

很裸對吧?

  1. 預處理估價函式

先跑一遍反向邊的預處理出每個點到1的最短路作為估價函式

  1. 直接跑(這裡用實現)

從n號點開始,用堆來代替佇列(實現上面所講的排序)
這時候先到1節點的肯定答案更優(也就是路徑更短)
原因很簡單吧:估價函式保證答案合法,而排序之後答案有序
搜到K個到達1節點的路徑就可以結束,快的飛起。。。

放個程式碼?

好不容易寫一次註釋

#include<bits/stdc++.h>
#define lst long long
#define ldb double
#define N 1050
#define M 10050
#define qw ljl[i].to
using namespace std;
const lst Inf=1e15;
int read()
{
    int s=0,m=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')m=1;ch=getchar();}
    while( isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return m?-s:s;
}
int n,m,K,Done;
bool in[N];
lst dis[N];
queue<int> Q;
int hd[N],cnt;
struct EDGE{int to,nxt,v;}ljl[M<<1];
void Add(int p,int q,int o){ljl[++cnt]=(EDGE){q,hd[p],o},hd[p]=cnt;}
void SPFA()
{
    for(int i=2;i<=n;++i)dis[i]=Inf;
    while(!Q.empty())Q.pop();
    Q.push(1),dis[1]=0,in[1]=true;
    while(!Q.empty())
    {
        int now=Q.front();Q.pop(),in[now]=false;
        for(int i=hd[now];i;i=ljl[i].nxt)
            if(qw>now&&dis[qw]>dis[now]+ljl[i].v)
            {
                dis[qw]=dis[now]+ljl[i].v;
                if(!in[qw])in[qw]=true,Q.push(qw);
            }
    }
}
//h[i]=g[i]+f[i]---->ans[i]=D+dis[i]
struct NODE{
    lst D;int id;
    bool operator<(const NODE &X) const
        {
            return D+dis[id]>X.D+dis[X.id];
        }
};priority_queue<NODE> H;
void A_star_Bfs()
{
    while(!H.empty())H.pop();
    H.push((NODE){0,n});
    while(!H.empty())
    {
        NODE temp=H.top();
        int now=temp.id;H.pop();
        if(now==1)
        {
            printf("%lld\n",temp.D);
            if(++Done==K)return;continue;
        }
        for(int i=hd[now];i;i=ljl[i].nxt)
            if(qw<now)H.push((NODE){temp.D+ljl[i].v,qw});
    }while(Done<K)++Done,puts("-1");
}
int main()
{
    n=read(),m=read(),K=read();
    for(int i=1;i<=m;++i)
    {
        int p=read(),q=read(),o=read();
        Add(p,q,o),Add(q,p,o);
    }
    SPFA(),A_star_Bfs();
    return 0;
}
/************
1.A*演算法在人工智慧中是一種典型的啟發式搜尋演算法
啟發中的估價是用估價函式表示的:
h(n)=f(n)+g(n)
其中f(n)是節點n的估價函式
g(n)表示實際狀態空間中從初始節點到n節點的實際代價
h(n)是從n到目標節點最佳路徑的估計代價。
另外定義h'(n)為n到目標節點最佳路徑的實際值。
如果h'(n)≥h(n)則如果存在從初始狀態走到目標狀態的最小代價的解
那麼用該估價函式搜尋的演算法就叫A*演算法。
2.第K最短路的演算法
我們設源點為s,終點為t,我們設狀態f(i)的g(i)為從s走到節點i的實際
距離,h(i)為從節點i到t的最短距離,從而滿足A*演算法的要求,
當第K次走到f(n-1)時表示此時的g(n-1)為第K最短路長度。
3.這裡是kuai的xzy的。。。別怪我。。。
*************/ 

總結

暫時就將這麼多吧
主要是看到網上沒有寫的那麼通俗的搜尋
就想自己總結一下(其實也不通俗。。。
撤撤撤溜了溜了*_*