poj 2449 Remmarguts' Date 【SPFA+Astar】【经典】

题目:poj 2449 Remmarguts‘ Date

题意:给出一个图,求k短路。

算法:SPFA求最短路 + AStar

下面引用大牛的分析:

首先,为了说话方便,列出一些术语:

启发式搜索中,对于每个状态 x,启发函数 f(x) 通常是这样的形式:

f(x) = g(x) + h(x)

其中 g(x) 是从初始状态走到 x 所花的代价;h(x)
是从 x 走到目标状态所需要的代价的估计值。

相对于 h(x),还有一个概念叫 h*(x),表示从 x 走到目标状态所需要的实际最小代价(当然,这个值有时我们是事先无法知道的)。

如果在你的启发函数里,能保证 h(x) <= h*(x),也就是说,你不能高估了从 x 走到目标状态所需要的代价,那就可以说这个搜索是 A* 算法(这里的“*”,英文就读作 star)。

A* 算法的特点是,如果存在从初始状态走到目标状态的最小代价的解,那么用 A* 算法搜索时,第一个找到的解就一定是最小代价的。这就是所谓的可采纳(admissible)。

1. 求前 K 短的 可以带环的 路径(的长度)

1.1. 典型的启发式搜索

设起点为 s;终点为 t;对于一个点 v,dt(v) 表示从 v 走到 t 的最短路径的长度(可以在初始化的时候全都算好)。

可以用最典型的启发式搜索来解这个问题。一个状态 x 表示的是从 s 走到某个点的一条路径,把这个点记作 x.v,把这条路径的长度记作 x.len。接着,我们可以使用以下启发函数:

g(x) = x.len;  h(x) = dt(x.v);

∴ f(x) = g(x) + h(x) = x.len + dt(x.v)

初始状态中, x.v = s; x.len
= 0。然后每次让优先队列(所谓的 Open
表)中 f(x) 值最小的状态 x
出队,再跟据图中所有从 x.v 出发的边发展下一层状态,让它们进队列。优先队列中不存在判重复的问题,因为每个状态所代表的路径肯定是不一样的。

不难想通,这是一个 A* 算法,因为这里的 h(x) 本身就是 h*(x),当然满足 h(x) <= h*(x)。因此可以说,在每次出队列的状态 x 中,第一次遇到 x.v == t 时,就找到了从 s 到 t 的第一短的路径,它的长度就是 f(x)……第 k 次遇到 x.v == t 时,就找到了从 s 到 t 的第 k 短的路径。

这就是传说中的启发式搜索,因为有启发函数,而且能够利用广搜的特性,不断的扩展。感觉是一个很经典的题目。

注意的是,假如起点和终点是同一点,因为第一次走到的就是,所以必须K++

继续膜拜大牛,上我写的挫代码:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int N = 1200;
const int inf = 0x3f3f3f3f;
struct Node
{
    int to,val;
};
vector<Node> g[N],rg[N];
int dis[N];
bool vis[N];
int n,m;
void SPFA(int src)
{
    int i;
    memset(dis,inf,sizeof(dis));
    dis[src] = 0;
    queue<int> Q;
    Q.push(src);
    while(!Q.empty())
    {
        int u,v;
        u = Q.front();
        Q.pop();
        for(int i = 0;i<g[u].size();i++)
        {
            v = g[u][i].to;
            if(dis[v]>dis[u]+g[u][i].val) //记得这里这样写  最后改一下vis
            {
                dis[v] = dis[u] + g[u][i].val;
                Q.push(v);
            }
        }
    }
}
struct Tree
{
    int v,len,h;
    bool operator < (const Tree & a) const
    {
        return a.h<h;
    }
};
priority_queue<Tree> que;
int Astar(int s,int t,int k)
{
    int cnt = 0;
    while(!que.empty())
        que.pop();
    if(s==t)
        k++;
    if(dis[s]==inf)
        return -1;
    Tree no,next;
    no = (Tree){s,0,dis[s]};
    que.push(no);
    while(!que.empty())
    {
        no = que.top();
        que.pop();
        //printf("%d %d %d \n",no.v,no.len,no.h);
        if(no.v == t)
            cnt++;
        if(cnt==k)
            return no.len;
        for(int i=0;i<rg[no.v].size();i++)
        {
            Node tmp = rg[no.v][i];
            next.v = tmp.to;
            next.len = no.len + tmp.val;
            next.h = next.len + dis[tmp.to];
            que.push(next);
        }
    }
    return -1;
}
void Clear(int x)
{
    for(int i=0;i<=x;i++){
        g[i].clear();
        rg[i].clear();
    }
}
int main()
{
    //freopen("Input.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0;i<m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            g[y].push_back((Node){x,z});
            rg[x].push_back((Node){y,z});
        }
        int s,t,k;
        scanf("%d%d%d",&s,&t,&k);
        SPFA(t);
        int ans = Astar(s,t,k);
        printf("%d\n",ans);
        Clear(n);
    }
    return 0;
}

poj 2449 Remmarguts' Date 【SPFA+Astar】【经典】

时间: 2024-10-08 07:46:03

poj 2449 Remmarguts' Date 【SPFA+Astar】【经典】的相关文章

poj 2449 Remmarguts&#39; Date A*+spfa求第k短路

题意: 经典的第k短路,A*算法的经典应用之一. 分析: A*,已走的路程g+到终点的最短距离为启发函数,搜索过程中不判重,第k次到t节点时就求出了第k短路. 代码: //poj 2449 //sep9 #include <iostream> #include <queue> using namespace std; const int maxN=1024; const int maxM=100024; int n,m,s,t,k,e,ne; int head[maxN],nhea

poj 2449 Remmarguts&#39; Date 求第k短路 Astar算法

=.=好菜 #include <iostream> #include <cstdio> #include <string.h> #include <cstring> #include <queue> using namespace std; const int N = 1e3+10; const int M = 100000+10; typedef long long ll; const ll INF = 1e15; int n,m,head[N

POJ 2449 Remmarguts&#39; Date【SPFA】【A*】

Remmarguts' Date Time Limit: 4000MS Memory Limit: 65536K Total Submissions: 21978 Accepted: 5982 Description "Good man never makes girls wait or breaks an appointment!" said the mandarin duck father. Softly touching his little ducks' head, he to

图论(A*算法,K短路) :POJ 2449 Remmarguts&#39; Date

Remmarguts' Date Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 25216   Accepted: 6882 Description "Good man never makes girls wait or breaks an appointment!" said the mandarin duck father. Softly touching his little ducks' head, h

poj 2449 Remmarguts&#39; Date (k短路模板)

Remmarguts' Date http://poj.org/problem?id=2449 Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 30772   Accepted: 8397 Description "Good man never makes girls wait or breaks an appointment!" said the mandarin duck father. Softly tou

POJ 2449 Remmarguts&#39; Date (A*搜索求K短路)

传送门 这是一道裸的K短路的问题,我们将会用A*解决. 我们设计估值函数h的时候可以像这样想.因为h(n) <= h*(n)而且要尽量接近h*(n),所以我们想到,可以求一个从目标节点到其余节点的最短路,这个一定是小于等于实际值的.然后就用A*从起点开始搜索,找到一个节点v,就使cnt[v]加1.当cnt[v] > k时就可以剪枝了,因为这一定不再K短路的路线上了.很好通过反证法得到证明.当目标节点被搜索到了第k次的时候就可以结束搜索了. 要注意这道题有一个很坑的地方,就是若给出的起点=终点,

(最短路+A*搜索)POJ 2449 - Remmarguts&#39; Date

题意: 给一个DAG,要求s到t的第K短路,很经典的问题. 分析: 我们可以看到k<=1000,这个值不是很大,我可以想到直接bfs走遍所有情况,最多也就有1000中情况,但是1000个点显然会M. 既然是要求k短路,也就是说最终计算出来的到达t的花费必然是递增的,也就是说我们在搜索的时候肯定要用到优先队列. 这时应该很明显了,必然需要A*搜索,但是A*的预估函数依然是个问题,我想了很久没有想到,后来偶然在一本书上发现了这题. 书上的处理是用dijsktra预处理每点到t的最短路,就是每个状态到

POJ——2449 Remmarguts&#39; Date

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

POJ 2449 Remmarguts&#39; Date

Remmarguts' Date Time Limit: 4000ms Memory Limit: 65536KB This problem will be judged on PKU. Original ID: 244964-bit integer IO format: %lld      Java class name: Main "Good man never makes girls wait or breaks an appointment!" said the mandari