学习记录——记忆化搜索 A* POJ2449 Remmarguts' Date

嗯,因为今天考试有个状压搜索可以用A*优化,以前也做过A*的题没有AC,于是今天就打算学一下A*(Astar)……

百度第一个文章其实就不错的,地址:戳我
建议大家先去看这篇文章。
那篇文章讲的确实不错,但是例题却是坑了点,曼哈顿距离对于平面图可以卡死,但是好的启发函数对于那个图来说预处理就可以找到最短路了(宽搜),所以文章很好,例题可以考虑换一个~

下面讲一些自己的理解:启发式搜索由两部分构成,g和h,g可以理解为对前面的总结,h可以理解为对未来的期望。启发函数就是 f = g + h;
那么启发式搜索为什么快呢?因为启发式搜索由启发函数,启发函数的大小决定了这个答案的优先与否,启发函数又与自己的选择有关,所以依据题目选择一个好的启发函数是很关键的。

其他细节上边博客都讲了,这里着重讲一下POJ2449 K短路
题目链接:戳我

题目大意:给定n个点,m条边,接下来m行,每行三个数,a,b,c表示a到b又一条权值为c的边,最后一行三个数,s,t,k表示求s道t的K短路。

题目解析:
K短路,A*经典题目。
首先,g我们不用想,就是前面的点的dist,然后h应该是什么呢?曼哈顿距离?这题显然可以,因为你不知道两个点的坐标,或许你可以离散化? 233我们不用这个不科学的估价函数。
首先我们要理解,估价函数是最优估计,并不是说要求K短路就用最K优估计的估价函数。
我们可以反向建边,从终点跑最短路,这样求的一套dist,这一套dist就是每个点的估价函数,因为它是每个点到end点的最短路,所以一定最优。
这时我们想,怎么求K短呢?当求得一条到达终点的路径时我们让路径K–,第一次一定是最短路,第二次由于最优估价函数的不会再次跑到,所以第二次就是次短路(非严格次短,因为有可能两条最短路,次短长度==最短),直到第K次求得就是最短路了。

那么f怎么转移呢?f=g+h,于是求得g和h就是求f了,如果用u表示当前点,v表示边Ei连的下一个点,dist数组存的是从终点到各点的最短路,那么u点的估价函数f - dist[u] = g[u](f - h = g) g[v] = g[u] + Ei.dist(Ei.dsit表示边Ei的权值,区别于前面的dist数组,这个dist是结构体里面的),f[v] = g[v] + h[v] = g[v] + dist[v],于是转移方程:f[v] = f[u] - dist[u] + Ei.dist + dist[v];
这样当k==0时 就是K短路了。

下面贴代码一份~(本人求dist数组时用的spfa,当然有的题目用dijkstra才是更好的,本题没有刻意卡spfa)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int size = 100010;
int st,en;
struct Edge{int to,dist;}edges[size],edges2[size];
int head[size],head2[size],next[size],next2[size],tot,tot2;
void build(int f,int t,int d)
{
    edges[++tot].to = t;
    edges2[++tot2].to = f;
    edges[tot].dist = d;
    edges2[tot2].dist = d;
    next[tot] = head[f];
    next2[tot2] = head2[t];
    head[f] = tot;
    head2[t] = tot2;
}
queue <int> qq;
int dist[size];
bool vis[size];
void spfa()
{
    memset(dist,63,sizeof(dist));
    qq.push(en);
    vis[en] = 1;
    dist[en] = 0;
    while(qq.size())
    {
        int f = qq.front();
        qq.pop();
        vis[f] = 0;
        for(int i = head2[f];i;i = next2[i])
        {
            int v = edges2[i].to;
            if(dist[v] > dist[f] + edges2[i].dist)
            {
                dist[v] = dist[f] + edges2[i].dist;
                if(!vis[v])
                {
                    vis[v] = 1;
                    qq.push(v);
                }
            }
        }
    }
}
struct AA{
    int u,f;
    bool operator < (const AA &rhs) const
    {
        return f > rhs.f;
    }
};
priority_queue <AA> q;
int k;
//int dist2[size];
int n,m;
void Astar()
{
    if(st == en) k++;
    q.push((AA){st,dist[st]});
    while(!q.empty())
    {
        AA f = q.top();
        q.pop();
        if(f.u == en)
        {
            if(!--k)
            {
                cout<<f.f;
                return ;
            }
        }
        for(int i = head[f.u];i;i = next[i])
        {
            int v = edges[i].to;
            q.push((AA){v,f.f-dist[f.u]+edges[i].dist+dist[v]});
        }
    }
    printf("-1");
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;i ++)
    {
        int f,t,d;
        scanf("%d%d%d",&f,&t,&d);
        build(f,t,d);
    }
    scanf("%d%d%d",&st,&en,&k);
    spfa();
    Astar();
    return 0;
}

/*
2 4
1 2 1
1 2 2 
1 2 3 
1 2 4
1 2 4
*/

如有问题:qq915648344,不定期在线,因为最近考试比较多~
(Orz FireStorm神犇)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值