ACM-ICPC 2018 沈阳赛区网络预赛 D. Made In Heaven(K短路+启发式A*)

127 篇文章 0 订阅
15 篇文章 0 订阅

Made In Heaven

问题分析

题意:求 1 1 n的第 k k 短的路径长度,如果超过T输出 Whitesnake! W h i t e s n a k e ! ,否则输出 yareyaredawa y a r e y a r e d a w a
题解:这道题呢,核心就在于对于 A A ∗ 算法的理解与应用。

这里不打算长述什么是 A A ∗ 算法。
只需要知道 A A ∗ 也是基于 bfs b f s 的就可以了,我们知道 bfs b f s 是针对每一个未访问的节点,用一个队列去扩展它,直到队列为空。但是 bfs b f s 是很傻的,只要对于未访问的节点,它都去拓展。 A A ∗ 之所以更优秀是因为它更聪明,它有一个从起点到任意顶点 n n 的实际距离g(n),还有一个对于任意顶点 n n 到目标顶点的估算距离h(n),那么 A A ∗ 就可以通过这两个函数去衡量优先拓展哪一个节点,我们称这个估算函数为 f(n)=h(n)+g(n) f ( n ) = h ( n ) + g ( n )

对于 f(n) f ( n ) 估价函数的设计,大大影响了算法的效率。

其中,当 h(n)0 h ( n ) ≠ 0 时,我们一定可以找到最优解, h(n) h ( n ) 越小,需要拓展的节点就越多;但是当 h(n)=0 h ( n ) = 0 时,算法就变成了普通的 dijkstra d i j k s t r a ,因为没有了对于目标顶点的估算。其次,如果 g(n)=0 g ( n ) = 0 呢?此时算法是最快的,可以很快的找到最短路径,但是,我们对实际距离没有了确定的值,找到的路却不一定正确,就相当于我们跑普通的 bfs b f s 找到了目测的最短路却忽略了其中可能会有障碍物。

大致理解了 A A ∗ 的估算函数就看代码吧~
这题有一个坑点,就是对于 T T 的比较,我们在执行A算法时, g(n) g ( n ) 就很大程度影响了我们的算法效率,我们实现函数 g(n) g ( n ) 时,可以将原图的边反向,把终点改成源点,跑一次 dijkstra d i j k s t r a ,算出每一个点 u u t的距离,即 g(n) g ( n ) 。当 g(n)>T g ( n ) > T 时,我们应忽略此节点,不去拓展。

代码

#include <bits/stdc++.h>
using namespace std;
const int Maxn = 10010, INF = 1e9+7;
struct node {
        int to,val;
        node() {
        }
        node(int a,int b)
        {
                to = a; val = b;
        }
};

vector<node> adj[Maxn],_adj[Maxn];

bool vis[Maxn];
int dis[Maxn],n,m,k;

void AddEdge(int x,int y,int val)
{
        adj[x].push_back(node(y,val));
        _adj[y].push_back(node(x,val));//把图反向
}
void Dijkstra(int s,int t)
{
        priority_queue<int, vector<int>, greater<int> > q;
        while(!q.empty()) q.pop();

        for(int i=1; i<=n; i++) vis[i] = false,dis[i] = INF;
        vis[t] = true; dis[t] = 0; q.push(t);

        int u,len;
        while(!q.empty())
        {
                u = q.top();  q.pop();
                len = _adj[u].size();
                for(int i=0; i<len; i++)
                {
                        node v = _adj[u][i];
                        if(dis[v.to] > dis[u] + v.val)
                        {
                                dis[v.to] = dis[u] + v.val;
                                if(!vis[v.to])
                                {
                                        q.push(v.to);
                                        vis[v.to] = true;
                                }
                        }
                }
                vis[u] = false;
        }
}

struct Anode {
        int h,g,id;
        Anode(int a,int b,int c) {
                h=a; g=b; id=c;
        }
        bool operator < (Anode a) const
        {
                return h+g > a.h+a.g;
        }
};

priority_queue<Anode> pq;

int BFS(int s,int t,int limit)
{
        while(!pq.empty()) pq.pop();
        pq.push(Anode(0,dis[s],s));

        int len,num;
        num = 0;
        while(!pq.empty())
        {
                Anode u = pq.top(); pq.pop();
                if(u.id==t) ++num;
                if(u.h > limit)
                        return -1;
                if(num>=k) return u.h;

                len = adj[u.id].size();
                for(int i=0; i<len; i++)
                {
                        node v = adj[u.id][i];
                        if(dis[v.to] > limit) //本题核心所在
                                continue;
                        pq.push(Anode(u.h+v.val,dis[v.to],v.to));
                }
        }

        return -1;
}

int main()
{
        while(~scanf("%d%d",&n,&m))
        {

                int x,y,v,s,t,limit;
                for(int i=0; i<Maxn; i++) adj[i].clear(),_adj[i].clear();
                scanf("%d%d%d%d",&s,&t,&k,&limit);
                for(int i=1; i<=m; i++)
                {
                        scanf("%d%d%d",&x,&y,&v);
                        AddEdge(x,y,v);
                }
                if(s==t) k++;
                Dijkstra(s,t);
                int ans = BFS(s,t,limit);
                if(ans > limit || ans == -1)
                        printf("Whitesnake!\n");
                else{
                        printf("yareyaredawa\n");
                }
        }
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值