【Dijkstra+邻接表求次短路】POJ Sightseeing 3463

Language:
Sightseeing
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 7766 Accepted: 2766

Description

Tour operator Your Personal Holiday organises guided bus trips across the Benelux. Every day the bus moves from one city S to another city F. On this way, the tourists in the bus can see the sights alongside the route travelled. Moreover, the bus makes a number of stops (zero or more) at some beautiful cities, where the tourists get out to see the local sights.

Different groups of tourists may have different preferences for the sights they want to see, and thus for the route to be taken from S to F. Therefore, Your Personal Holiday wants to offer its clients a choice from many different routes. As hotels have been booked in advance, the starting city Sand the final city F, though, are fixed. Two routes from S to F are considered different if there is at least one road from a city A to a city B which is part of one route, but not of the other route.

There is a restriction on the routes that the tourists may choose from. To leave enough time for the sightseeing at the stops (and to avoid using too much fuel), the bus has to take a short route from S to F. It has to be either a route with minimal distance, or a route which is one distance unit longer than the minimal distance. Indeed, by allowing routes that are one distance unit longer, the tourists may have more choice than by restricting them to exactly the minimal routes. This enhances the impression of a personal holiday.

For example, for the above road map, there are two minimal routes from S = 1 to F = 5: 1 → 2 → 5 and 1 → 3 → 5, both of length 6. There is one route that is one distance unit longer: 1 → 3 → 4 → 5, of length 7.

Now, given a (partial) road map of the Benelux and two cities S and F, tour operator Your Personal Holiday likes to know how many different routes it can offer to its clients, under the above restriction on the route length.

Input

The first line of the input file contains a single number: the number of test cases to follow. Each test case has the following format:

  • One line with two integers N and M, separated by a single space, with 2 ≤ N ≤ 1,000 and 1 ≤ M ≤ 10, 000: the number of cities and the number of roads in the road map.

  • M lines, each with three integers AB and L, separated by single spaces, with 1 ≤ AB ≤ NA ≠ B and 1 ≤ L ≤ 1,000, describing a road from city A to city B with length L.

    The roads are unidirectional. Hence, if there is a road from A to B, then there is not necessarily also a road from B to A. There may be different roads from a city A to a city B.

  • One line with two integers S and F, separated by a single space, with 1 ≤ SF ≤ N and S ≠ F: the starting city and the final city of the route.

    There will be at least one route from S to F.

Output

For every test case in the input file, the output should contain a single number, on a single line: the number of routes of minimal length or one distance unit longer. Test cases are such, that this number is at most 109 = 1,000,000,000.

Sample Input

2
5 8
1 2 3
1 3 2
1 4 5
2 3 1
2 5 3
3 4 2
3 5 4
4 5 3
1 5
5 6
2 3 1
3 2 1
3 1 10
4 5 2
5 2 7
5 2 7
4 1

Sample Output

3
2

Hint

The first test case above corresponds to the picture in the problem description.

Source



题意:

旅行团从S地出发到达T地,为了省油要求尽量走最短路径或比最短路径长1单位距离的路径,求满足条件的路径条数

解题思路:

相信最短路很容易求出来的,主要是次短路如何求出???

我们可以先求出从S到T的次短路的距离,然后判断这个距离与最短距离是否相差为一?所以我们的任务就变成了求解次短路和最短路的问题。

(s,u,v)代表从s——v的最短路,u是中间一个必经点。

我们先看看最短路和次短路的比较:

①(s,u,v)最短路=到达前一个点u的最短路+(u,v)的最短路。

而最短路求解有两种情况:

①(s,u,v)次短路=到达前一个点u的次短路+(u,v)的最短路。

②(s,u,v)次短路=到达前一个点u的最短路+(u,v)的次短路。

所以依据次短路的求解条件,可以在求解最短路的时候求解次短路。

我们用:d[ v ][ 0 ]代表最短路,d[ v ] [ 1 ]代表次短路。 

(一)如果从s—u的最短路加上(u,v)权值的距离小于原来的d[ v ][ 0 ] ,我们就可把次短路d[ v ][ 1 ] 的值更新为d[ v ][ 0 ],就该题而言,此时可以把次短路的条数也更新为这个点的最短路的条数;把当前最短路d[ v ][ 0 ]的值更新成最小的,和原来最短路的松弛操作是一样的。

(二)如果从s—u的最短路加上(u,v)权值的距离大于原来的d[ v ][ 0 ],这就说明这条路路径就可能是一条次短路,那我们需要判断这条路的值dist,和原来的次短路的值d[ v ][1]进行比较!

①如果它比原来的次短路要大,直接跳过。

②如果它比原来的次短路要小,那么我们就需要更新最短路的长度。

③如果与原来的次短路相等,说明我们的次短路有另一种(这里并不一定是只有1种)方法可以到达终点v。这里要加的种类是:原来到v点的次短路的条数+到u点的次短路(或者最短路,(u,v)并不知道是不是一定是最短路,如果是次短路,那么此时加的就是到u的最短路)的条数。


次短路的情况已经分析出来,最短路的情况是比较容易得出了的,和Dijkstra操作正常的松弛操作就可以了,不过本题让求路径条数,所以要在松弛操作出现相等情况时进行相加操作,这一点比较简单,不在分析,详细看代码。


  AC代码:

#include <stdio.h>
#include <math.h>
#include <vector>
#include <queue>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#define INF 0x3f3f3f3f

using namespace std;

const int MAXN = 10010;
const int MAXM = 100010;

struct Edge
{
    int from;
    int to;
    int w;
    int next;
}edge[MAXN];

int tot;
int head[MAXM];

void addEdge(int u,int v,int w)
{
    edge[tot].from=u;
    edge[tot].to=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot++;
}

int d[MAXM][2];//记录最短路、次短路距离。
int c[MAXN][2];//记录最短路、次短路路径条数。
int vis[MAXN][2];//记录最短路、次短路时该点是否被访问过。
int N,M;

void Dijkstra(int s)
{
    int flag;
    int k;
    for(int i=1;i<=N;i++){
        c[i][0]=c[i][1]=0;
        d[i][0]=d[i][1]=INF;
        vis[i][0]=vis[i][1]=0;
    }
    d[s][0]=0;
    c[s][0]=1;
    int min_dist=INF;
    while(true){
        int v=-1;
        for(int i=1;i<=N;i++){ //我们并不能确定取出的最小值的就一定是最短路的点
            for(int j=0;j<2;j++){//所以要遍历次短和最短的点。
                if(!vis[i][j]&&(v==-1||d[i][j]<d[v][k])){
                    v=i;
                    k=j;//记录这个点是次短还是最短
                }
            }
        }
        if(v==-1) break;
        vis[v][k]=1;
        for(int i=head[v];i!=-1;i=edge[i].next){
            int j=edge[i].to;
            int dist=d[v][k]+edge[i].w;;
            if(dist<d[j][0]){//dist小于最短路径长。
                d[j][1]=d[j][0];//次短路径长  
                c[j][1]=c[j][0];//次短路径路径数目 
                d[j][0]=dist;//最短路径长 
                c[j][0]=c[v][k];//最短路径路径数目
            }
            else if(dist==d[j][0]){//dist等于最短路径长:
                c[j][0]+=c[v][k];//最短路径路径数目 
            }
            else if(dist<d[j][1]){//dist大于最短路径长且小于次短路径长: 
                d[j][1]=dist;//次短路径长 
                c[j][1]=c[v][k];//次短路径路径数目 
            }
            else if(dist==d[j][1]){//dist等于次短路径长:  
                c[j][1]+=c[v][k];//次短路径路径数目
            }
        }
    }
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        tot=0;
        memset(head,-1,sizeof(head));
        scanf("%d%d",&N,&M);
        int u,v,w;
        for(int i=0;i<M;i++){
            scanf("%d%d%d",&u,&v,&w);
            addEdge(u,v,w);
        }
        int s,g;
        scanf("%d%d",&s,&g);
        Dijkstra(s);
        int res=c[g][0];
        if(d[g][1]==d[g][0]+1){
            res+=c[g][1];
        }
        printf("%d\n",res);
    }
    return 0;
}


  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用邻接实现Dijkstra算法的C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTEX_NUM 100 // 最大顶点数 #define INF INT_MAX // 无穷大 // 边的结构体 typedef struct Edge { int dest; // 目标顶点编号 int weight; // 权重 struct Edge *next; // 下一条边指针 } Edge; // 邻接中顶点的结构体 typedef struct Vertex { Edge *head; // 第一条边 } Vertex; // 图的结构体 typedef struct Graph { Vertex vertices[MAX_VERTEX_NUM]; // 顶点数组 int vertex_num; // 顶点数 } Graph; // 初始化图 void init_graph(Graph *graph, int vertex_num) { graph->vertex_num = vertex_num; for (int i = 0; i < vertex_num; i++) { graph->vertices[i].head = NULL; } } // 添加边 void add_edge(Graph *graph, int src, int dest, int weight) { Edge *edge = (Edge *) malloc(sizeof(Edge)); edge->dest = dest; edge->weight = weight; edge->next = graph->vertices[src].head; graph->vertices[src].head = edge; } // Dijkstra算法 void dijkstra(Graph *graph, int start, int *dist, int *prev) { int visited[MAX_VERTEX_NUM] = {0}; // 是否已经访问 for (int i = 0; i < graph->vertex_num; i++) { dist[i] = INF; // 到各个顶点的距离都设置为无穷大 prev[i] = -1; // 各个顶点的前驱顶点都设置为-1 } dist[start] = 0; // 起点到自身的距离为0 for (int i = 0; i < graph->vertex_num; i++) { int min_dist = INF; int u = -1; // 找到未访问顶点中距离最近的顶点 for (int j = 0; j < graph->vertex_num; j++) { if (!visited[j] && dist[j] < min_dist) { min_dist = dist[j]; u = j; } } if (u == -1) { break; } visited[u] = 1; // 标记为已访问 // 更新与顶点u相邻的顶点的距离 for (Edge *edge = graph->vertices[u].head; edge != NULL; edge = edge->next) { int v = edge->dest; int w = edge->weight; if (!visited[v] && dist[u] + w < dist[v]) { dist[v] = dist[u] + w; prev[v] = u; } } } } // 打印最短路径 void print_path(Graph *graph, int start, int end, int *prev) { if (start == end) { printf("%d", start); } else if (prev[end] == -1) { printf("无路径"); } else { print_path(graph, start, prev[end], prev); printf("->%d", end); } } int main() { Graph graph; init_graph(&graph, 5); add_edge(&graph, 0, 1, 10); add_edge(&graph, 0, 3, 30); add_edge(&graph, 0, 4, 100); add_edge(&graph, 1, 2, 50); add_edge(&graph, 2, 4, 10); add_edge(&graph, 3, 2, 20); add_edge(&graph, 3, 4, 60); int dist[MAX_VERTEX_NUM]; int prev[MAX_VERTEX_NUM]; dijkstra(&graph, 0, dist, prev); for (int i = 1; i < graph.vertex_num; i++) { printf("0到%d的最短距离为%d,路径为", i, dist[i]); print_path(&graph, 0, i, prev); printf("\n"); } return 0; } ``` 该代码使用邻接存储图,实现了Dijkstra算法,可以解任意两个顶点之间的最短路径。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值