A*和K短路

A*

ps:作者是蒟蒻,可能有些地方是错误的,欢迎批评指正。

思想

走迷宫怎么走?用BFS就可以,但是BFS需要把所有步数<=答案的节点全部入队。
A*和K短路_第1张图片
我们会发现这样遍历有很多没用的节点也入队了,能不能让这些节点不入队?这里有一个想法,就是只让最接近T(曼哈顿距离小)的节点入队,那么就变成这样了:
A*和K短路_第2张图片
好像很对?其实是错的,因为迷宫可以有障碍,一旦有障碍,这个想法就会被毙掉。
A*和K短路_第3张图片
黑色的是障碍,会发现S到不了T。
(其实前面都是free话)
A*的想法是,让不是最“接近T”(有了障碍物之后曼哈顿距离不一定是实际距离,所以加双引号)的节点也能入队,但是最“接近T”的节点先用来遍历,这样既可以减少入队数(虽然入队数还是可能很大,但是肯定小了很多),又可以解决之前的问题。A*也可以用来求边权不同的最短路,只要让节点能重复入队即可。

上述“接近T”被称为估价函数,视不同题目而定(比如走迷宫就可以用曼哈顿距离)格式如下:
f(n)=g(n)+h(n)
f(n)表示经过n并到达T的估计代价。
g(n)表示从S到n的实际代价。
h(n)表示从n到T的估计代价(启发式函数)。
那么f(n)越符合题意A*就越先用来遍历。
A*是一种启发式搜索,复杂度玄学,h(n)越接近实际代价,效率越高。

需要注意的是,由于f(n)只是估计代价,所以有时先到T的不一定的是真正最优的,所以要从所有到T的状态中刷出最优解才是答案(除非h(n)就是实际代价,那就没必要刷出最优解了)。

K短路

思想

最短路比较简单,但次短路就很难了,常用最短路方法无法派上用场,更恶心的是还有K短路。

其实K短路能用A*轻松解决!我们知道A*会让最“优先”的节点先遍历,所以先到T的路径常常比较短。而启发式函数h(n)我们是可以做到实际代价的:用最短路算法预处理每一个点到T的距离。这样A*每一步都是最优秀的,也就是说当第K次走到T时,就是K短路。

因为启发式函数是最好的,所以效率不错,但时间和空间复杂度还是玄学(=_=|||)。

模板

以POJ2449为例。
完全裸的K短路,只不过这道题有毛病,如果s==t不走不算一种方案,所以s==t时K要+1。

#include
#include
#include
#include
using namespace std;
const int maxn=1000,maxm=100000,maxk=1000;

int n,m,st,gl,K,tot;
int E[2],lnk[2][maxn+5],nxt[2][maxm+5],son[2][maxm+5],w[2][maxm+5];
int h[maxn+5],que[maxn+5];
bool vis[maxn+5];
struct data
{
    int x,g;
    data(int X=0,int G=0) {x=X;g=G;}
    bool operator < (const data &a) const {return g+h[x]>a.g+h[a.x];}
};
priority_queue heap_s; //用优先队列来保证f小的先用来遍历

bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
    int tot=0,f=1;char ch=getchar(),lst='+';
    while ('9''0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    x=tot*f;
    return Eoln(ch);
}
void Add(int id,int x,int y,int z)
{
    son[id][++E[id]]=y;w[id][E[id]]=z;
    nxt[id][E[id]]=lnk[id][x];lnk[id][x]=E[id];
}
void Spfa() //Spfa预处理h(n)
{
    memset(vis,0,sizeof(vis));memset(h,63,sizeof(h));
    int Head=0,Tail=0;que[++Tail]=gl;vis[gl]=true;h[gl]=0;
    while (Head!=Tail)
    {
        int x=que[Head=(Head+1)%maxn];vis[x]=false;
        for (int j=lnk[1][x];j;j=nxt[1][j])
            if (h[x]+w[1][j]1][j]])
            {
                h[son[1][j]]=h[x]+w[1][j];
                if (!vis[son[1][j]])
                {
                    que[Tail=(Tail+1)%maxn]=son[1][j];vis[son[1][j]]=true;
                    if (h[que[Tail]]1)%maxn]])
                        swap(que[Tail],que[(Head+1)%maxn]);
                }
            }
    }
}
void A_star()
{
    while (!heap_s.empty()) heap_s.pop();
    heap_s.push(data(st,0));
    while (!heap_s.empty())
    {
        data x=heap_s.top();heap_s.pop();
        if (x.x==gl&&++tot==K) {printf("%d\n",x.g);break;} //第K次
        for (int j=lnk[0][x.x];j;j=nxt[0][j])
            heap_s.push(data(son[0][j],x.g+w[0][j]));
    }
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    readi(n);readi(m);
    for (int i=1;i<=m;i++)
    {
        int x,y,z;readi(x);readi(y);readi(z);
        Add(0,x,y,z);Add(1,y,x,z);
    }
    readi(st);readi(gl);readi(K);if (st==gl) K++;
    Spfa();A_star();
    if (totprintf("-1\n");
    return 0;
}

你可能感兴趣的:(Astar和K短路,算法&数据结构总结By_ZZK)