A*與IDA*


謹以此文向人工智能先驅,\(A\)*算法發明者\(Nils\ Nilsson\)致敬

一篇深入研究的博客,而本文更多是粗略理解和習題吧。

\(A\)*算法是什么?它是啟發式搜索的一種,即廣度搜索算法\(bfs\)加上估價函數。

\(IDA\)*則是另一種類似的啟發式搜索,是迭代加深\(dfs\)加上估價函數。

迭代加深搜索是什么?就是每次限制\(dfs\)的深度進行搜索。

然后我們來討論該算法的核心部分:估價函數

估價函數假設用\(h(x)\)表示,到達目前狀態的代價用\(g(x)\)表示,那么估計的完成代價為\(f(x)=g(x)+h(x)\)。如果\(f(x)\)大於題目所需,那么可以進行剪枝。

需要注意的是,\(h(x)\)必須小於等於實際最優代價,滿足此條件下越接近實際代價該估價函數越優。

一道例題:SCOI2005 騎士精神

對於一種狀態,最佳的方案(運氣最好時)是每次都能使一個騎士到達預定位置,最后一次能使一個騎士和一個空格到達預定位置,所以估價函數\(h(x)=\)棋盤上與目標狀態不同的位置數\(+1\)

然后搜索,注意每次改變空格的位置即可。

提供一份\(IDA\)*的代碼

#include<bits/stdc++.h>
using namespace std;
const int dx[]={-2,-2,-1,-1,1,1,2,2}, dy[]={-1,1,-2,2,-2,2,-1,1};
const int tar[5][5]=
{
    {2, 2, 2, 2, 2},
    {1, 2, 2, 2, 2},
    {1, 1, 0, 2, 2},
    {1, 1, 1, 1, 2},
    {1, 1, 1, 1, 1}
};
int sta[5][5];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}

int evoluate()
{
    int res=0;
    for (int i=0; i<5; i++)
        for (int j=0; j<5; j++)
            if (tar[i][j]^sta[i][j]) res++;
    return res;
}

bool IDA(int x, int y, int dep, int maxdep)
{
    int evo=evoluate();
    if (dep+evo>maxdep+1) return false;
    if (!evo) return true;
    for (int i=0; i<8; i++)
    {
        int nx=x+dx[i], ny=y+dy[i];
        if (nx>=0 && nx<5 && ny>=0 && ny<5)
        {
            swap(sta[x][y], sta[nx][ny]);
            if (IDA(nx, ny, dep+1, maxdep)) return true;
            swap(sta[x][y], sta[nx][ny]);
        }
    }
    return false;
}

int main()
{
    int T=read();
    while (T--)
    {
        char s[5][5]; int X, Y;
        for (int i=0; i<5; i++) scanf("%s", s[i]);
        for (int i=0; i<5; i++)
            for (int j=0; j<5; j++)
            {
                char c=s[i][j]; 
                if (c=='0') sta[i][j]=1;
                if (c=='1') sta[i][j]=2;
                if (c=='*') X=i, Y=j, sta[i][j]=0;
            }
        int ans=-1;
        for (int i=1; i<=15; i++)
            if (IDA(X, Y, 0, i)) {ans=i; break;}
        printf("%d\n", ans);
    }
    return 0;
}

一道類似的題:八數碼難題

本題可以用\(IDA\)*和雙向\(bfs\)通過,甚至\(map\)去重的\(bfs\)也可通過。作為一道練手題還是不錯的。

代碼不放了,和上題大體一致(我才不會說是我懶)

再看一個\(A\)*經典應用:\(K​\)短路

(洛谷的模板題\(A​\)*只能得到\(92pts​\),標算是可持久化可並堆,所以這里就當是理性愉悅了吧)

說一下\(A​\)*算法的思路。

先在反向圖中\(Dijkstra\)得到估價函數\(dis(i)\)

然后\(bfs\),用堆維護一下目前距離\(f(i)+dis(i)\)就可以了。

有一個沒有什么用的優化,遍歷每個點的次數不會超過\(\frac{w}{dis(1)}\),可以剪枝。

\(92pts\)代碼

#include<bits/stdc++.h>
using namespace std;
const int N=5005, M=200005;
struct node{int to, nxt; double w;}edge1[M], edge2[M];
struct Node{int id; double w;};
struct NODE{int id; double w, f;};
bool operator < (Node a, Node b){return a.w>b.w;}
bool operator < (NODE a, NODE b){return a.f>b.f;}
int head1[N], head2[N], vis[N], cnt[N], cnt1, cnt2, n, m, ans;
double W, dis[N];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}

void add(int u, int v, double w)
{
    edge1[++cnt1]=(node){v, head1[u], w};
    head1[u]=cnt1;
    edge2[++cnt2]=(node){u, head2[v], w};
    head2[v]=cnt2;
}

void Dijkstra(int S)
{
    priority_queue<Node> q; q.push((Node){S, 0});
    for (int i=1; i<=S; i++) dis[i]=1e12; dis[S]=0.0;
    memset(vis, 0, sizeof(vis));
    while (!q.empty())
    {
        int u=q.top().id; q.pop();
        if (vis[u]) continue; vis[u]=1;
        for (int i=head2[u]; i; i=edge2[i].nxt)
        {
            int v=edge2[i].to; double w=edge2[i].w;
            if (dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                q.push((Node){v, dis[v]});
            }
        }
    }
}

void A_star(int S, int Max_cnt)
{
    priority_queue<NODE> q;	q.push((NODE){S, 0, 0});
    while (!q.empty())
    {
        NODE u=q.top(); q.pop(); int x=u.id;
        if (u.w>W) return;
        cnt[x]++;
        if (x==n) {ans++; W-=u.f; continue;}
        if (cnt[x]>Max_cnt) continue;
        for (int i=head1[x]; i; i=edge1[i].nxt)
        {
            int v=edge1[i].to;
            q.push((NODE){v, u.w+edge1[i].w, u.w+edge1[i].w+dis[v]});
        }
    }
}

int main()
{
    n=read(); m=read(); scanf("%lf", &W);
    for (int i=1; i<=m; i++)
    {
        int u=read(), v=read(); 
        double w; scanf("%lf", &w);
        add(u, v, w);
    }
    Dijkstra(n); A_star(1, W/dis[1]);
    printf("%d\n", ans);
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM