1. 程式人生 > >BZOJ 1598 淺談AstaR啟發式搜尋有向圖網路K階最短路

BZOJ 1598 淺談AstaR啟發式搜尋有向圖網路K階最短路

這裡寫圖片描述
世界真的很大
今天考了字串
沒看空限被86M卡空間唉,下次一定注意了
草草地學了一下這個什麼Astar演算法,也不算是完全瞭解吧就找了這道題來做做
Astar好像聽說在AI方面有很多運用,但是隻是在競賽中的話一般用做搜尋的剪枝和順序處理
這道題體現的是後者

看題先:

description:

BESSIE準備用從牛棚跑到池塘的方法來鍛鍊. 但是因為她懶,她只准備沿著下坡的路跑到池塘, 然後走回牛棚. BESSIE也不想跑得太遠,所以她想走最短的路經. 農場上一共有M (1 <= M <= 10,000)條路, 每條路連線兩個用1..N(1 <= N <= 1000)標號的地點. 更方便的是,如果X>Y,則地點X的高度大於地點Y的高度. 地點N是BESSIE的牛棚;地點1是池塘. 很快, BESSIE厭倦了一直走同一條路.所以她想走不同的路,更明確地講,她想找出K (1 <= K <= 100)條不同的路經.為了避免過度勞累,她想使這K條路經為最短的K條路經. 請幫助BESSIE找出這K條最短路經的長度.你的程式需要讀入農場的地圖, 一些從X_i到Y_i 的路經和它們的長度(X_i, Y_i, D_i). 所有(X_i, Y_i, D_i)滿足(1 <= Y_i < X_i; Y_i < X_i <= N, 1 <= D_i <= 1,000,000).

input:

  • 第1行: 3個數: N, M, 和K

  • 第 2..M+1行: 第 i+1 行包含3個數 X_i, Y_i, 和 D_i, 表示一條下坡的路.

output:

  • 第1..K行: 第i行包含第i最短路經的長度,或-1如果這樣的路經不存在.如果多條路經有同樣的長度,請注意將這些長度逐一列出.

注意是n是起點,1是終點,一開始搞錯了然後過不了樣例233
首先跑K遍SPFA肯定不現實,因為一來時間大,二來不好實現
考慮直接爆搜
這樣可以把每一條路徑都搜出來
如果我們在每一次搜尋的時候都能去搜索“當前還沒有走過的路徑的最小值”,那搜K次豈不是最短路了?
那麼問題轉化成暴力搜尋最短路
總不能把所有路徑搜出來排個序吧?
那麼就是說在搜尋最短路的時候,需要一個“導向型”的方法以完成剪枝

這個就是所說的Astar演算法的“估價函式”了
由Astar演算法引入估價函式來完成最短路的搜尋的優化,保證每一次搜尋都是儘量往短的路走,然後搜尋K次就是前K短路了

問題在於這裡的“估價函式”從何而來?
Astar演算法的“估價函式”一般都是一個比較值,一個估計值,估計的越準確,效率就越高
這裡需要估計的是什麼呢?自然就是還剩的距離了
在選擇方向時,一個點還剩的距離遠小,這條路就越短,顯然
當然還需要考慮當前已經實際走過的距離
即是說:
f(i) = g(i)+h(i)
這裡g是實際走過的值,h是還要走的最短距離
本來A*是一個很玄的東西就是因為估價函式的準度直接影響搜尋效率和順序,但是在這裡不存在
因為估計的值我們是可以直接算出來的,即某個點到1的最短路
這個可以建立反圖SPFA,Dij什麼的預處理得到
這樣估計值就進化成了準確值,我們的Astar搜尋時就不會有半點偏差

如果不是很好理解的話就想像一下Astar搜尋最短路時的情景吧
在選擇點前進的時候,哪個點離1近就選哪個,這樣走出來肯定是最短路啊
然後搜尋到一次之後重新搜尋的時候,也是選擇次小的方案,因為最小的已經選過了,那麼就會選擇當前最小的重新搜尋,自然是次小值
如此重複K次之後就是答案了

講道理Astar其實就是一個剪枝,並不是什麼單獨的演算法,更是一種思想一樣的東西
所以說其用的就是一個普通的BFS而已
但當然有一點不同
對一個點處理的時候一定要在其在隊首的時候處理,不能在入隊之前處理
由於這道題要求了“前K小”的順序,只有在經過f值估價排序後才能決定處理順序,如果直接在搜尋到的時候處理就會變成建邊順序,這個肯定不對

按f值優先選擇只需要把佇列換成堆或者優先佇列就好了

完整程式碼:

#include<stdio.h>
#include<map>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef long long dnt;

const int INF=0x3f3f3f3f;

struct edge
{
    int v,last,w;
}ed[200010],ad[200010];

struct cow
{
    int id;
    dnt g,h;
    friend bool operator > (const cow &a,const cow &b)
    {
        return a.g+a.h > b.g+b.h;
    }
};

priority_queue <cow, vector<cow>,greater<cow> > state;
queue <int> q;

int n,m,K,num=0,mum=0,cnt=0;
int head[100010],hed[100010];
dnt dis[100010],far[100010],a[100010];
int se[100010],src[100010];

cow Make(dnt g,dnt h,int id)
{
    cow tmp;
    tmp.g=g,tmp.h=h,tmp.id=id;
    return tmp;
}

void add(int u,int v,int w)
{
    num++;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].last=head[u];
    head[u]=num;
}

void ade(int u,int v,int w)
{
    mum++;
    ad[mum].v=v;
    ad[mum].w=w;
    ad[mum].last=hed[u];
    hed[u]=mum;
}

void SPFA(int S)
{
    memset(dis,INF,sizeof(dis));
    q.push(S),dis[S]=0,se[S]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop(),se[u]=0;
        for(int i=hed[u];i;i=ad[i].last)
        {
            int v=ad[i].v;
            if(dis[v]>dis[u]+ad[i].w)
            {
                dis[v]=dis[u]+ad[i].w;
                if(!se[v]) se[v]=1,q.push(v);
            }
        }
    }
}

void Astar(int S)
{
    state.push(Make(0,dis[S],S));
    while(!state.empty())
    {
        int u=state.top().id;
        dnt w=state.top().g;
        state.pop();
        if(u==1) a[++cnt]=w;
        if(cnt==K) return ;
        for(int i=head[u];i;i=ed[i].last)
        {
            int v=ed[i].v;
            state.push(Make(w+ed[i].w,dis[v],v));
        }
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w),ade(v,u,w);
    }
    SPFA(1);
    memset(a,-1,sizeof(a));
    Astar(n);
    for(int i=1;i<=K;i++)
        printf("%lld\n",a[i]);
    return 0;
}
/*
EL PSY CONGROO
*/

嗯,就是這樣