A*学习笔记 && POJ 2449

来源:互联网 发布:智通方达 软件多少钱 编辑:程序博客网 时间:2024/05/22 03:15

A*(A-Star)算法是一种静态路网中求解最短路最有效的直接搜索方法,也是许多其他问题的常用启发式算法。(度娘百科←)

嗯嗯,关于A*,就是一种通过改变搜索方式来尽可能降低时间复杂度,获得最优解的方法。

对于一个状态n,有一个函数f(n)=g(n)+h(n)

g(n)代表从起始状态到状态n的实际代价
h(n)代表从状态n到最终状态的估计代价
f(n)=g(n)+h(h)代表从起始状态到最终状态的估计代价

关于h(n)的选取(来自度娘百科)
保证找到最短路径(最优解的)条件,关键在于估价函数f(n)的选取(或者说h(n)的选取)。
我们以d(n)表达状态n到目标状态的距离,那么h(n)的选取大致有如下三种情况:
如果h(n)<= d(n)到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。
如果 h(n)>d(n),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。

如果h(n)==,当前的代码就极类似于BFS…
Dijkstra算法和A* 本质是相同的,只有一点不同,就是Dijkstra算法没有启发式(H值总是)。由于没有启发式,它在各个方向上平均搜索。正如你所预料,由于Dijkstra算法在找到目标前通常会探索更大的区域,所以一般会比A*更慢一些。

说白了,A*就是种贪心吧。

讲了这么多,如果看不懂的话….自行百度or@Euk。
PS:用A*需谨慎!一个不好的估价函数有可能导致你整个程序WA掉!
———————————————————分割线—————————————————————
Remmarguts’ Date
Time Limit: 4000MS Memory Limit: 65536K
Total Submissions: 28056 Accepted: 7619
Description

“Good man never makes girls wait or breaks an appointment!” said the mandarin duck father. Softly touching his little ducks’ head, he told them a story.

“Prince Remmarguts lives in his kingdom UDF – United Delta of Freedom. One day their neighboring country sent them Princess Uyuw on a diplomatic mission.”

“Erenow, the princess sent Remmarguts a letter, informing him that she would come to the hall and hold commercial talks with UDF if and only if the prince go and meet her via the K-th shortest path. (in fact, Uyuw does not want to come at all)”

Being interested in the trade development and such a lovely girl, Prince Remmarguts really became enamored. He needs you - the prime minister’s help!

DETAILS: UDF’s capital consists of N stations. The hall is numbered S, while the station numbered T denotes prince’ current place. M muddy directed sideways connect some of the stations. Remmarguts’ path to welcome the princess might include the same station twice or more than twice, even it is the station with number S or T. Different paths with same length will be considered disparate.
Input

The first line contains two integer numbers N and M (1 <= N <= 1000, 0 <= M <= 100000). Stations are numbered from 1 to N. Each of the following M lines contains three integer numbers A, B and T (1 <= A, B <= N, 1 <= T <= 100). It shows that there is a directed sideway from A-th station to B-th station with time T.

The last line consists of three integer numbers S, T and K (1 <= S, T <= N, 1 <= K <= 1000).
Output

A single line consisting of a single integer number: the length (time required) to welcome Princess Uyuw using the K-th shortest path. If K-th shortest path does not exist, you should output “-1” (without quotes) instead.
Sample Input

2 2
1 2 5
2 1 4
1 2 2
Sample Output

14
手动翻译一下…

题目描述
给你一张n点m条单向边的图,让你求s到t的第k短路。
输入输出略
样例解释:
这里写图片描述
求1到2的第2短路,
1–>2–>1–>2
5+4+5=14

翻译完了。

怎么做呢?

最短路直接用floyd,dijkstra,SPFA,就行了。
次短路加俩if加个附加数组。
K短路怎么办?并且1 <= K <= 1000;

还记得 dijkstra+堆 么?它采取贪心的策略,优先选择权值小的,从起点跑到终点的权值一定是最短路。

那,用dijkstra能做吧?
这里写图片描述
不够优,优先队列炸了。

那么我们就应该考虑A*了。

再写一遍:
f(n)=g(n)+h(n)

g(n)代表从起始状态到状态n的实际代价
h(n)代表从状态n到最终状态的估计代价
f(n)=g(n)+h(h)代表从起始状态到最终状态的估计代价

g(n)通过DJ的性质能直接求出来。
那h(n)呢?

显然,当h(n)==t到n的最短路时,h(n)最优。

那么,反向建边,跑一遍t到所有点的最短路。
然后h就求出来了。

在跑DJ的时候,按照 权值+h(n) 小到大排序。

这样,就能求K短路了。

为什么呢?

前面说过:
还记得 dijkstra+堆 么?它采取贪心的策略,优先选择权值小的,从起点跑到终点的权值一定是最短路。

那么,第二次跑到终点的点,权值和一定比最短路大,比其他点到最短路的权值和小对吧,那它就是次短路。
以此类推,第3短,第4短……第k短就都能求出来了。

这里要注意一个细节…
这里写图片描述
对于这张图,我们可以清楚的看出来,
1–>4的最短路是2,次短路是5,三短路是101。
所以我想说什么呢?
我们要记录目标点被访问的次数,一旦目标点的访问次数为≥k次,那就说明有第k短路了。
如果,你一遇到目标点,就更新答案的话,会WA的。
比如上图。
如果是按照:碰到目标点的次数。
那么第二次碰到4的是2,权值为100的那条边。
显然是错误的。
所以,我们要在记录目标点被访问的次数时,目标点出堆一次,访问次数++。
在这个地方WA了好几次….

#include<cstdio>#include<queue>using namespace std;const int maxn=100000*2;const int maxm=1500;const int inf=0x7fffffff;int h[maxm];struct heap{    int p;    int dis;    bool operator < (const heap &hah)const    {        return dis+h[p] > hah.dis+h[hah.p];    } };int n,m,ss,tt,k;priority_queue<heap>q;struct Edge{    int to;    int d;    int next;}edge[maxn],edge1[maxn];bool vis[maxm];int head[maxn],head1[maxn];int tot;void add(int f,int t,int d){    edge[++tot].to=t;    edge[tot].d=d;    edge[tot].next=head[f];    head[f]=tot;} int tot1;void add1(int f,int t,int d){    edge1[++tot1].to=t;    edge1[tot1].d=d;    edge1[tot1].next=head1[f];    head1[f]=tot1;} queue<int>q1;void spfa(){    for(int i=1;i<=n;i++)        h[i]=inf;    q1.push(tt);    h[tt]=0;    while(!q1.empty())    {        int x=q1.front();        vis[x]=0;        q1.pop();        for(int i=head1[x];i;i=edge1[i].next)        {            Edge e=edge1[i];            if(h[e.to]>h[x]+e.d)            {                h[e.to]=h[x]+e.d;                if(!vis[e.to])                {                    vis[e.to]=1;                    q1.push(e.to);                }            }        }    }}bool flag=false;void DJ() {    q.push((heap){ss,0});    int cnt=0;    while(!q.empty())    {        heap p=q.top();        q.pop();        if(p.p==tt)        {            cnt++;            if(cnt==k)            {                flag=1;                printf("%d",p.dis);                return;            }        }        for(int i=head[p.p];i;i=edge[i].next)        {            int v=edge[i].to;            q.push((heap){v,p.dis+edge[i].d});        }    }}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++)    {        int a,b;        int c;        scanf("%d%d%d",&a,&b,&c);        add(a,b,c);        add1(b,a,c);    }    scanf("%d%d%d",&ss,&tt,&k);    if(ss==tt)        k++;    spfa();    DJ();    if(!flag)        puts("-1");    return 0;}
0 0
原创粉丝点击