【POJ 2449】Remmarguts' Date(A*+dij求k短路)

qwq
原题面,英文emmmm……
想看中文版可以滑到下面去√

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
———————————————————
咳咳咳
图省事直接拿份队内胡策题目题面【跑】

翻译题面鸣谢
feather MeiCo Black学长和Summer 学姐qwq
swc学长不要打我qwq题不是我出的qwq

题目描述

众所周知,赛小城是一个胸怀大志,对游泳情有独钟的美男子。
他第一次尝试游泳便是在广州的大海边。不知怎的,在沙滩上,由于古老东方的神秘力量,他被吸入了一个新的海底聚落。召唤他的是南海龙王,因为他想为自己选定一个继承人。但管理海底世界需要一定的组织能力。于是他为赛小城出了一个题:若有n个城市和m条单向道路,城市编号为1~n。每条道路连接两个不同的城市,n和m满足m <= n(n - 1)。给定两个城市a和b,可以给a到b的所有简单路(所有城市最多经过一次,包括起点和终点)排序:先按长度从小到大排序,长度相同时按照字典序从小到大排序。管理南海一直是赛小城儿时的梦,他的任务便是求出从a城市到b城市的第k短路。
作为一名社会主义的好青年,助他一臂之力是你义不容辞的责任。

输入描述

输入的第一行为两个正整数 n 和 m,表示有 n 种化学物质和 m 条单向边。
接下来 m 行,每行有 3 个正整数 x,y,z,表示从化学物质x和化学物质y之间的单向边长度为 z
接下来一行,包括三个整数,a,b,k 表示赛小城所在的位置 a,终点所在的位置 b,和 k

输出描述

如果赛小城能通过第 k 短路从 S 到达 E,输出一个正整数,为第 k 短路的长度,如果不能通过第 k短路到达 E,输出“-1”(不包括引号)

样例输入

2 2
1 2 5
2 1 4
1 2 2

样例输出

14

数据范围及提示

对于 30%的数据 k <= 2
对于 70%的数据 1 <= N <= 100, 0 <= M <= 1000 1 <= a, b <= N, 1 <= c <= 100,k <= 100
对于 100%的数据 1 <= N <= 1000, 0 <= M <= 100000 1 <= a, b <= N, 1 <= c <= 100,k<=1000

思路:
由于堆优化dijkstra贪心+终点的出堆次数就为次数短路 我们可以记一个cnt记录出堆次数 判断一下得到答案。但是 这样会TLE 因为我们这一算法的复杂度与k和n相关
我们用A*优化,公式表示为: f(n)=g(n)+h(n)
其中 f(n) 是从初始点经由节点 n 到目标点的估价函数, g(n) 是在状态空间中从初始节点到 n 节点的实际代价, h(n) 是从 n 到目标节点最佳路径的估计代价。
即估价函数 = 当前值 + 当前位置到终点的距离
用spfa来确定真正的最短的长度。因为题目是一个有向图,那么我们可以建一个反向图,这里建图的时候用了两个邻接链表/链式前向星?分开建,之后来记录一下终点到起点的最短路, 这样我们的估价就有一个准确的参考了,最起码用SPFA估价会我们的答案会更接近正解而不是远离它。

代码:
(这里是胡策代码,输入没有弄上多组输入,浏览的诸位请千万注意修改一下,不要直接扔上OJ)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int sz = 233333;
int h[sz],n,m,S,E,k;
bool vis[sz],flag;

int tot1,fir1[sz],nxt1[sz];
int tot,fir[sz],nxt[sz];

struct A
{
    int pos;
    int g;
    bool operator < (const A &a) const
    {
        return g + h[pos] > a.g + h[a.pos];
    }
};
struct ed{
    int f,t,w;
}l[sz],l1[sz];

inline void build(int f,int t,int d)
{
    l[++tot] = (ed){f,t,d};
    nxt[tot] = fir[f];
    fir[f] = tot; 
} 
inline void build1(int f,int t,int d)
{
    l1[++tot1] = (ed){f,t,d};
    nxt1[tot1] = fir1[f];
    fir1[f] = tot1; 
}
queue<int>q1;
void spfa()
{
    memset(h,0x3f3f3f,sizeof(h));
    q1.push(E);
    h[E] = 0;
    while(!q1.empty())
    {
        int u = q1.front();
        vis[u] = 0;
        q1.pop();
        for(int i = fir1[u];i;i = nxt1[i])
        {
            int v = l1[i].t;
            if(h[v] > h[u] + l1[i].w)
            {
                h[v] = h[u] + l1[i].w;
                if(!vis[v])
                {
                    vis[v]=1;
                    q1.push(v);
                }
            }
        }
    }
}
priority_queue<A>q;
void Astar()
{
/*  
    A t;t.pos=S,t.g=0;
    q.push(t);
    这里的写法和下面的那一行一样的
*/
    q.push((A){S,0});
    int cnt = 0;
    while(!q.empty())
    {
        A u = q.top();
        q.pop();
        if(u.pos==E)
        {
            cnt ++;
            if(cnt == k)
            {
                flag = 1;
                printf("%d",u.g);
                return;
            }
        }
        for(int i = fir[u.pos];i;i = nxt[i])
        {
            int v = l[i].t;
            q.push((A){v,u.g + l[i].w});
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        build(a,b,c);
        build1(b,a,c);
    }
    scanf("%d%d%d",&S,&E,&k);
    if(S==E) k++;
    spfa();
    Astar();
    if(flag==0)
        puts("-1");
    return 0;
}

emmmm在之后还有一个从网上摘下来分析中的版本,qwq感觉不如队里传统码风好理解
也附上:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
const int sz = 100010;
const int inf =10000000;
using namespace std;
struct ed{
    int to,w,nxt;
}l[sz<<1]; 
struct data{
    int g,h;
    int to;
    bool operator < (data a) const
    {
        return a.h+a.g<h+g;
    }
};
int tot,n,S,E,head[sz],tail[sz],dis[sz];
inline void build(int f,int t,int v)
{
    l[tot].to=t;
    l[tot].w=v;
    l[tot].nxt=head[f];
    head[f]=tot++;

    l[tot].to=f;
    l[tot].w=v;
    l[tot].nxt=tail[t];
    tail[t]=tot++;
}
void dij()
{
    int vis[sz];
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    dis[E]=0;
    int k;
    for(int h=1;h<=n;++h)
    {
        k=-1;
        int min=inf;
        for(int j=1;j<=n;++j)
        if(!vis[j]&&min>dis[j])
        {
            k=j;
            min=dis[j];
        }
//  if(k==-1)
//      break;
    vis[k]=1;
    for(int i=tail[k];i!=-1;i=l[i].nxt)
    {
        int v=l[i].to;
        if(!vis[v]&&dis[v]>dis[k]+l[i].w)
            dis[v]=dis[k]+l[i].w;
    }
    }
}
int A(int k)
{
    int cnt[sz];
    data now,nxt;
    priority_queue<data>q;
    memset(cnt,0,sizeof(cnt));
    now.to=S,now.g=0,now.h=dis[S];
    q.push(now);
    while(!q.empty())
    {
        now=q.top();
        q.pop();
        cnt[now.to]++;
        if(cnt[now.to]>k)
            continue;
        if(cnt[E]==k)
            return now.g;
        for(int i=head[now.to];i!=-1;i=l[i].nxt)
        {
            int v=l[i].to;
            nxt.to=v;
            nxt.g=now.g+l[i].w;
            nxt.h=dis[v];
            q.push(nxt) ;
        }
    }
    return -1;
}
int m,u,v,w,k;
int main()
{
    while (~scanf("%d%d",&n,&m))
    {
        tot=0;
        memset(head,-1,sizeof(head));
        memset(tail,-1,sizeof(tail));
        while(m--)
        {
            cin>>u>>v>>w;
            build(u,v,w);
        }
        cin>>S>>E>>k;
        if(S==E)
            k++;
        dij();
        cout<<A(k)<<'\n';
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值