A*和K短路

来源:互联网 发布:jsp java注释 编辑:程序博客网 时间:2024/04/29 04:18

A*

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

思想

走迷宫怎么走?用BFS就可以,但是BFS需要把所有步数<=答案的节点全部入队。
这里写图片描述
我们会发现这样遍历有很多没用的节点也入队了,能不能让这些节点不入队?这里有一个想法,就是只让最接近T(曼哈顿距离小)的节点入队,那么就变成这样了:
这里写图片描述
好像很对?其实是错的,因为迷宫可以有障碍,一旦有障碍,这个想法就会被毙掉。
这里写图片描述
黑色的是障碍,会发现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<cstdio>#include<cstring>#include<algorithm>#include<queue>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<data> 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'<ch||ch<'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]<h[son[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]]<h[que[(Head+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 (tot<K) printf("-1\n");    return 0;}
0 0
原创粉丝点击