07-图5 Saving James Bond - Hard Version

题目:

This time let us consider the situation in the movie “Live and Let Die” in which James Bond, the world’s most famous spy, was captured by a group of drug dealers. He was sent to a small piece of land at the center of a lake filled with crocodiles. There he performed the most daring action to escape – he jumped onto the head of the nearest crocodile! Before the animal realized what was happening, James jumped again onto the next big head… Finally he reached the bank before the last crocodile could bite him (actually the stunt man was caught by the big mouth and barely escaped with his extra thick boot).

Assume that the lake is a 100 by 100 square one. Assume that the center of the lake is at (0,0) and the northeast corner at (50,50). The central island is a disk centered at (0,0) with the diameter of 15. A number of crocodiles are in the lake at various positions. Given the coordinates of each crocodile and the distance that James could jump, you must tell him a shortest path to reach one of the banks. The length of a path is the number of jumps that James has to make.

Input Specification:
Each input file contains one test case. Each case starts with a line containing two positive integers N (≤100), the number of crocodiles, and D, the maximum distance that James could jump. Then N lines follow, each containing the (x,y) location of a crocodile. Note that no two crocodiles are staying at the same position.

Output Specification:
For each test case, if James can escape, output in one line the minimum number of jumps he must make. Then starting from the next line, output the position (x,y) of each crocodile on the path, each pair in one line, from the island to the bank. If it is impossible for James to escape that way, simply give him 0 as the number of jumps. If there are many shortest paths, just output the one with the minimum first jump, which is guaranteed to be unique.

Sample Input 1:
17 15
10 -21
10 21
-40 10
30 -50
20 40
35 10
0 -10
-25 22
40 -40
-30 30
-10 22
0 11
25 21
25 10
10 10
10 35
-30 10
Sample Output 1:
4
0 11
10 21
10 35
Sample Input 2:
4 13
-12 12
12 12
-12 -12
12 -12
Sample Output 2:
0

手绘示意图:
基本逻辑和easy版本的一样。
在这里插入图片描述

算法逻辑与分析:
1.根据输入构造图
1.1顶点和边的表达
1.1.1顶点包括鳄鱼,湖心岛中心和湖岸;注:需要去除在岛上,岸边的鳄鱼。
为何要对顶点和邻接表排序?
根据题目输出要求:优选有最小第一条的路径。
If there are many shortest paths, just output the one with the minimum first jump,
which is guaranteed to be unique.
对顶点按从近到远排序和对邻接表按从近到远排序,保证优先处理“最小第一跳”。

1.1.2对于顶点和邻接表用typedef结构数组的方式表示,而不用结构数组指针,更方便调试。
1.1.3边是动态生成的,所以没有必要用Edge结构表达和传递
1.1.4边是跳跃距离D,根据跳跃距离,连接顶点,构造图

2.对图求最短路径
典型的无权单源最短路径算法,一点改动是需要对当前的顶点做判断,是否已经是出口

3.输出最小路径
自定义堆栈输出

代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

#define MaxVertexNum 101 // 最多100条鳄鱼加上湖心岛
#define DistToBank 50
#define IslandRadius 7.5
#define NoExit -1

typedef int Vertex;

// 队列
typedef struct _Queue *Queue;
struct _Queue
{
    Vertex *Data;
    int front;
    int rear;
};

// 鳄鱼坐标(Coordinate Of Crocodile)
typedef struct _Coordinate
{
    int x, y;
} Coordinate[MaxVertexNum];

// 邻接点
typedef struct _AdjNode *AdjNode;
struct _AdjNode
{
    Vertex AdjV;
    AdjNode Next;
};

// 邻接表
typedef struct _AdjTable
{
    AdjNode FirstEdge;
} AdjTable[MaxVertexNum];

// 领接表表示的图
typedef struct _LGraph *LGraph;
struct _LGraph
{
    int Nv, Ne, Weight;    // 因为每跳是固定的值,所以Weight放在这里处理更简单
    Coordinate Crocodiles; // 鳄鱼坐标结构数组
    AdjTable Graph;        // 图
};

Queue createQ(int Nv);
void addQ(Queue Q, Vertex V);
Vertex delQ(Queue Q);
bool isEmpty(Queue Q);

LGraph createGraph(Coordinate Crocodiles, int Nv, int Weight);
bool isOnIsland(int x, int y);
bool isOnBank(int x, int y);
void sortVertexFromNearToFar(Coordinate Crocodiles, int Nv);
void sortAdjTableFromSmallToLarge(AdjNode FirstEdge);
bool isEdge(LGraph G, Vertex V, Vertex W);
void insertEdge(LGraph G, Vertex V, Vertex W);
bool isExit(LGraph G, Vertex V);
LGraph buildGraph();
Vertex BFS(LGraph G, Vertex S, int dist[], int path[]);
void save007(LGraph G);
void showPath(LGraph G, Vertex ExitID, int dist[], int path[]);
void freeGraph(LGraph G);

/*
输入1:
17 15
10 -21
10 21
-40 10
30 -50
20 40
35 10
0 -10
-25 22
40 -40
-30 30
-10 22
0 11
25 21
25 10
10 10
10 35
-30 10

输出1:
4
0 11
10 21
10 35

输入2:
4 13
-12 12
12 12
-12 -12
12 -12

输出2:
0
 */

int main()
{
    LGraph G = buildGraph();
    save007(G);
    freeGraph(G);

    return 0;
}

// 队列相关操作集
Queue createQ(int Nv)
{
    Queue Q = (Queue)malloc(sizeof(struct _Queue));
    Q->Data = malloc(Nv * sizeof(int));
    Q->front = Q->rear = -1;
    return Q;
}

void addQ(Queue Q, Vertex V)
{
    Q->rear++;
    Q->Data[Q->rear] = V;
}

Vertex delQ(Queue Q)
{
    Q->front++;

    return Q->Data[Q->front];
}

bool isEmpty(Queue Q)
{
    return Q->front == Q->rear ? true : false;
}

// 建图
LGraph buildGraph()
{ // N鳄鱼数量(顶点数),D跳跃距离(边的权重),(x,y)鳄鱼坐标,CrocodilesInLake有效的鳄鱼数
    int N, D, x, y, CrocodilesInLake = 0;
    scanf("%d %d", &N, &D);
    // 读入鳄鱼坐标
    Coordinate Crocodiles;

    for (int i = 0; i < N; i++)
    {
        scanf("%d %d", &x, &y);
        // 剔除不在湖里的鳄鱼
        if (!isOnIsland(x, y) && !isOnBank(x, y))
        {
            Crocodiles[CrocodilesInLake].x = x;
            Crocodiles[CrocodilesInLake].y = y;
            CrocodilesInLake++;
        }
    }

    // 将顶点从近到远排序
    sortVertexFromNearToFar(Crocodiles, CrocodilesInLake);

    // 建立只含顶点的初始图
    LGraph G = createGraph(Crocodiles, CrocodilesInLake, D);

    // 插入边
    Vertex V, W;
    for (V = 0; V < G->Nv - 1; V++)
    {
        for (W = V + 1; W < G->Nv; W++)
        {
            if (isEdge(G, V, W))
            { // 因为V,W不是固定的,而Weight又是固定的,所以没有必要先构造Edge,再插入
                insertEdge(G, V, W);
            }
        }
    }

    // 邻接表排序
    for (V = 0; V < G->Nv; V++)
    {
        sortAdjTableFromSmallToLarge(G->Graph[V].FirstEdge);
    }
    // 完成图的创建
    return G;
}

// 在岛上的鳄鱼
bool isOnIsland(int x, int y)
{
    float dist = sqrt(pow(x, 2) + pow(y, 2));

    if (dist <= IslandRadius)
    {
        return true;
    }
    else
    {
        return false;
    }
}

// 在岸上的鳄鱼
bool isOnBank(int x, int y)
{
    if (abs(x) >= DistToBank || abs(y) >= DistToBank)
    {
        return true;
    }
    else
    {
        return false;
    }
}

// 将顶点从近到远排序[选择排序]
void sortVertexFromNearToFar(Coordinate Crocodiles, int Nv)
{
    int i, j, min;
    // Coordinate Temp;
    struct _Coordinate Temp;
    for (i = 0; i < Nv - 1; i++)
    {
        min = i;
        for (j = i + 1; j < Nv; j++)
        {
            if (pow(Crocodiles[min].x, 2) + pow(Crocodiles[min].y, 2) >
                pow(Crocodiles[j].x, 2) + pow(Crocodiles[j].y, 2))
            {
                min = j;
            }
        }
        if (min != i)
        {
            Temp = Crocodiles[i];
            Crocodiles[i] = Crocodiles[min];
            Crocodiles[min] = Temp;
        }
    }
}

// 建立只含顶点的初始图
LGraph createGraph(Coordinate Crocodiles, int Nv, int Weight)
{
    LGraph G = (LGraph)malloc(sizeof(struct _LGraph));
    G->Nv = Nv + 1; // 将小岛也作为一个顶点归入图中,所以+1;
    G->Ne = 0;
    G->Weight = Weight;
    G->Crocodiles[0].x = G->Crocodiles[0].y = 0; // 小岛坐标

    Vertex V;
    for (V = 1; V < G->Nv; V++)
    {
        G->Crocodiles[V] = Crocodiles[V - 1]; // 读入顶点
    }

    for (V = 0; V < G->Nv; V++)
    {
        G->Graph[V].FirstEdge = NULL;
    }

    return G;
}

// 判断两个顶点是否有边
bool isEdge(LGraph G, Vertex V, Vertex W)
{
    bool ret = false;
    float dist = sqrt(pow(G->Crocodiles[W].x - G->Crocodiles[V].x, 2) +
                      pow(G->Crocodiles[W].y - G->Crocodiles[V].y, 2));
    if (V == 0)
    { // 小岛要单独考虑
        if (dist - IslandRadius <= G->Weight)
        {
            ret = true;
        }
    }
    else
    {
        if (dist <= G->Weight)
        {
            ret = true;
        }
    }

    return ret;
}

// 将边插入图中
void insertEdge(LGraph G, Vertex V, Vertex W)
{
    AdjNode newNode;

    newNode = (AdjNode)malloc(sizeof(struct _AdjNode));
    newNode->AdjV = W;
    newNode->Next = G->Graph[V].FirstEdge;
    G->Graph[V].FirstEdge = newNode;
    // 用邻接表表示无权图,1条边要插入2次
    newNode = (AdjNode)malloc(sizeof(struct _AdjNode));
    newNode->AdjV = V;
    newNode->Next = G->Graph[W].FirstEdge;
    G->Graph[W].FirstEdge = newNode;

    G->Ne++;
}

void sortAdjTableFromSmallToLarge(AdjNode FirstEdge)
{
    Vertex Temp;
    AdjNode p, q;

    for (p = FirstEdge; p; p = p->Next)
    {
        for (q = p->Next; q; q = q->Next)
        {
            if (q->AdjV < p->AdjV)
            {
                Temp = q->AdjV;
                q->AdjV = p->AdjV;
                p->AdjV = Temp;
            }
        }
    }
}

// 判断是否是出口
bool isExit(LGraph G, Vertex V)
{
    bool ret = false;

    if (V == 0)
    { // 能否从岛上跳到岸边?
        if ((DistToBank - IslandRadius) <= G->Weight)
        {
            ret = true;
        }
    }
    else if (DistToBank - abs(G->Crocodiles[V].x) <= G->Weight ||
             DistToBank - abs(G->Crocodiles[V].y) <= G->Weight)
    {
        ret = true;
    }

    return ret;
}

Vertex BFS(LGraph G, Vertex S, int dist[], int path[])
{
    Vertex V, ExitID = NoExit;
    AdjNode W;
    dist[S] = 0;
    Queue Q = createQ(G->Nv);
    addQ(Q, S);
    while (!isEmpty(Q))
    {
        V = delQ(Q);
        // 由于是递增的遍历,所以第一个找到的出口一定是首跳最短路径出口,可以break出来
        if (isExit(G, V))
        {
            ExitID = V;
            break;
        }

        for (W = G->Graph[V].FirstEdge; W; W = W->Next)
        {
            if (dist[W->AdjV] == -1)
            {
                dist[W->AdjV] = dist[V] + 1;
                path[W->AdjV] = V;
                addQ(Q, W->AdjV);
            }
        }
    }

    free(Q->Data);
    free(Q);

    return ExitID;
}

// 拯救007
void save007(LGraph G)
{
    int dist[G->Nv], path[G->Nv];
    Vertex V;
    for (V = 0; V < G->Nv; V++)
    {
        dist[V] = path[V] = -1;
    }
    // BFS遍历寻找出口,并带出出口值
    Vertex ExitID = BFS(G, 0, dist, path);

    // 按题目要求输出
    if (ExitID == NoExit)
    {
        printf("0\n");
    }
    else
    {
        showPath(G, ExitID, dist, path);
    }
}

// 利用堆栈输出具体路径
void showPath(LGraph G, Vertex ExitID, int dist[], int path[])
{
    int minJumps = dist[ExitID] + 1; // 要加上最后从出口鳄鱼跳上岸的那一步
    printf("%d\n", minJumps);

    Vertex Stack[G->Nv];
    int Top = -1;

    while (ExitID != -1)
    {
        Top++;
        Stack[Top] = ExitID;
        ExitID = path[ExitID];
    }
    Top--; // 最上面存的是小岛的坐标,不需要输出
    while (Top != -1)
    {
        printf("%d %d\n", G->Crocodiles[Stack[Top]].x, G->Crocodiles[Stack[Top]].y);
        Top--;
    }
}

void freeGraph(LGraph G)
{
    AdjNode p, q;
    Vertex V;

    for (V = 0; V < G->Nv; V++)
    {
        for (p = G->Graph[V].FirstEdge; p; p = q)
        {
            q = p->Next;
            free(p);
        }
    }
    // G->Crocodiles和G->Graph不是通过malloc分配的空间,所以无需用free释放
    //  free(G->Crocodiles);
    //  free(G->Graph);
    free(G);
}

测试结果:
在这里插入图片描述
小结:
非常精彩的一道题,是无权图单源最短路的一个典型应用,需要对该算法有有深刻的认识,并灵活应用。

注:
本文参考了
鸿雁丨红豆灬 的MOOC 07-图5 Saving James Bond - Hard Version
在此向大佬表示感谢。

2023.8.16, V2修订版,对实现逻辑做了进一步的厘清,重点解释了为何要对顶点和边排序,同时规范了变量,函数的命名。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>

#define MAX_NUM_CROCODILES 101 // 最多100条鳄鱼加上湖心岛
#define DISTANCE_TO_BANK 50
#define ISLAND_RADIUS 7.5
#define NO_EXIT -1

typedef int Vertex;

// 队列
typedef struct _Queue *Queue;
struct _Queue
{
    Vertex *elements;
    int front;
    int rear;
};

// 鳄鱼坐标(Coordinate Of Crocodile)
typedef struct _Coordinate
{
    int x, y;
} Coordinate[MAX_NUM_CROCODILES];

// 邻接点
typedef struct _AdjNode *AdjNode;
struct _AdjNode
{
    Vertex adjV;
    AdjNode next;
};

// 邻接表
typedef struct _AdjTable
{
    AdjNode firstEdge;
} AdjTable[MAX_NUM_CROCODILES];

// 领接表表示的图
typedef struct _Lake *Lake;
struct _Lake
{
    int Nv, Ne, maxJumpDist; // maxJumpDist最大每跳跳跃距离,相当于边的权重
    Coordinate crocodiles;   // 鳄鱼坐标数组
    AdjTable lake;          // 图
};

Queue createQ(int Nv);
void addQ(Queue Q, Vertex V);
Vertex delQ(Queue Q);
bool isEmpty(Queue Q);

Lake createLake(Coordinate crocodiles, int Nv, int maxJumpDist);
bool isOnIsland(int x, int y);
bool isOnBank(int x, int y);
void sortCrocodilesFromNearToFar(Coordinate crocodiles, int Nv);
void sortAdjCrocodilesFromNearToFar(AdjNode firstEdge);
bool isJumpable(Lake L, Vertex V, Vertex W);
void connectCrocodiles(Lake L, Vertex V, Vertex W);
bool isExit(Lake L, Vertex V);
Lake buildLake();
Vertex findExit(Lake L, Vertex S, int dist[], int path[]);
void save007(Lake L);
void showPath(Lake L, Vertex ExitID, int dist[], int path[]);
void freeGraph(Lake L);

/*
07-图5 Saving James Bond - Hard Version

难度:3颗星

算法逻辑:
1.根据输入构造图
1.1顶点和边的表达
1.1.1顶点包括鳄鱼,湖心岛中心和湖岸;注:需要去除在岛上,岸边的鳄鱼。
为何要对顶点和邻接表排序?
因为答案不唯一,结果和对数据处理的顺序有关。为了让结果唯一,题目对输出有2点要求:
a.优选最小第一跳的路径。
If there are many shortest paths, just output the one with the minimum first jump,
 which is guaranteed to be unique.
b.路径上的点,要按照岛到岸边“从近到远”的顺序输出
output the position (x,y) of each crocodile on the path, each pair in one line, from the island to the bank.

对顶点按从近到远排序使得离岛近的鳄鱼排在G->Crocodiles数组前面。
---顶点可以归结为,满足firstJump和不满足firstJump,经过对顶点按距离岛中心进行排序,则可以保证,
满足minimum firstJump的点排前面,如果它还满足shortest path,则满足条件a.

对邻接表按从近到远排序优先处理序号小【离岛近】的鳄鱼。
---对满足a的顶点的邻居节点按离岛远近排序,则可以保证路径上离岛近的顶点被优先输出,即满足条件b。

二者共同解决了a,b这2点要求。

1.1.2对于顶点和邻接表用typedef结构数组的方式表示,而不用结构数组指针,更方便调试。
1.1.3边是动态生成的,所以没有必要用Edge结构表达和传递
1.1.4边是跳跃距离D,根据跳跃距离,连接顶点,构造图

2.对图求最小路径
典型的无权单源最短路径算法,一点改动是需要对当前的顶点做判断,是否已经是出口

3.输出最小路径
利用堆栈输出

dist.png/path.png是执行后的结果截图。


17 15
10 -21
10 21
-40 10
30 -50
20 40
35 10
0 -10
-25 22
40 -40
-30 30
-10 22
0 11
25 21
25 10
10 10
10 35
-30 10

4
0 11
10 21
10 35

4 13
-12 12
12 12
-12 -12
12 -12

0
 */

int main()
{
    Lake L = buildLake();
    save007(L);
    freeGraph(L);

    return 0;
}

// 队列相关操作集
Queue createQ(int Nv)
{
    Queue Q = (Queue)malloc(sizeof(struct _Queue));
    Q->elements = malloc(Nv * sizeof(int));
    Q->front = Q->rear = -1;
    return Q;
}

void addQ(Queue Q, Vertex V)
{
    Q->rear++;
    Q->elements[Q->rear] = V;
}

Vertex delQ(Queue Q)
{
    Q->front++;

    return Q->elements[Q->front];
}

bool isEmpty(Queue Q)
{
    return Q->front == Q->rear ? true : false;
}

// 建图
Lake buildLake()
{ // N鳄鱼数量(顶点数),D最大跳跃距离(边的权重),(x,y)鳄鱼坐标,CrocodilesInLake有效的鳄鱼数
    int N, D, x, y, crocodilesInLake = 0;
    scanf("%d %d", &N, &D);
    // 读入鳄鱼的坐标
    Coordinate crocodiles;

    for (int i = 0; i < N; i++)
    {
        scanf("%d %d", &x, &y);
        // 剔除不在湖里的鳄鱼
        if (!isOnIsland(x, y) && !isOnBank(x, y))
        {
            crocodiles[crocodilesInLake].x = x;
            crocodiles[crocodilesInLake].y = y;
            crocodilesInLake++;
        }
    }

    // 将鳄鱼从近到远排序
    sortCrocodilesFromNearToFar(crocodiles, crocodilesInLake);

    // 建立只含顶点的初始图
    Lake L = createLake(crocodiles, crocodilesInLake, D);

    // 插入边
    Vertex V, W;
    for (V = 0; V < L->Nv - 1; V++)
    {
        for (W = V + 1; W < L->Nv; W++)
        {
            if (isJumpable(L, V, W))
            { // 因为V,W不是固定的,而maxJumpDist又是固定的,所以没有必要先构造Edge,再插入
                connectCrocodiles(L, V, W);
            }
        }
    }

    // 对临近的鳄鱼按从近到远的方式排序(邻接表排序)
    for (V = 0; V < L->Nv; V++)
    {
        sortAdjCrocodilesFromNearToFar(L->lake[V].firstEdge);
    }
    // 完成图的创建
    return L;
}

// 在岛上的鳄鱼
bool isOnIsland(int x, int y)
{
    return sqrt(x * x + y * y) <= ISLAND_RADIUS;
}

// 在岸上的鳄鱼
bool isOnBank(int x, int y)
{
    return (abs(x) >= DISTANCE_TO_BANK || abs(y) >= DISTANCE_TO_BANK);
}

// 将鳄鱼从近到远排序[选择排序]
void sortCrocodilesFromNearToFar(Coordinate crocodiles, int Nv)
{
    int i, j, min;
    struct _Coordinate temp;
    for (i = 0; i < Nv - 1; i++)
    {
        min = i;
        for (j = i + 1; j < Nv; j++)
        {
            if (pow(crocodiles[min].x, 2) + pow(crocodiles[min].y, 2) >
                pow(crocodiles[j].x, 2) + pow(crocodiles[j].y, 2))
            {
                min = j;
            }
        }
        if (min != i)
        {
            temp = crocodiles[i];
            crocodiles[i] = crocodiles[min];
            crocodiles[min] = temp;
        }
    }
}

// 建立只含顶点的初始图
Lake createLake(Coordinate crocodiles, int Nv, int maxJumpDist)
{
    Lake L = (Lake)malloc(sizeof(struct _Lake));
    L->Nv = Nv + 1; // 将小岛也作为一个顶点归入图中,所以+1;
    L->Ne = 0;
    L->maxJumpDist = maxJumpDist;
    L->crocodiles[0].x = L->crocodiles[0].y = 0; // 小岛坐标

    Vertex V;
    for (V = 1; V < L->Nv; V++)
    {
        L->crocodiles[V] = crocodiles[V - 1]; // 导入鳄鱼坐标
    }

    for (V = 0; V < L->Nv; V++)
    {
        L->lake[V].firstEdge = NULL;
    }

    return L;
}

// 判断两个鳄鱼之间是否可以跳跃(顶点是否有边)
bool isJumpable(Lake L, Vertex V, Vertex W)
{
    bool ret = false;
    float dist = sqrt(pow(L->crocodiles[W].x - L->crocodiles[V].x, 2) +
                      pow(L->crocodiles[W].y - L->crocodiles[V].y, 2));
    if (V == 0)
    { // 小岛要单独考虑
        if (dist - ISLAND_RADIUS <= L->maxJumpDist)
        {
            ret = true;
        }
    }
    else
    {
        if (dist <= L->maxJumpDist)
        {
            ret = true;
        }
    }

    return ret;
}

// 将鳄鱼之间的联系(边)插入图中
void connectCrocodiles(Lake L, Vertex V, Vertex W)
{
    AdjNode newNode;

    newNode = (AdjNode)malloc(sizeof(struct _AdjNode));
    newNode->adjV = W;
    newNode->next = L->lake[V].firstEdge;
    L->lake[V].firstEdge = newNode;
    // 用邻接表表示无权图,1条边要插入2次
    newNode = (AdjNode)malloc(sizeof(struct _AdjNode));
    newNode->adjV = V;
    newNode->next = L->lake[W].firstEdge;
    L->lake[W].firstEdge = newNode;

    L->Ne++;
}

void sortAdjCrocodilesFromNearToFar(AdjNode firstEdge)
{
    Vertex temp;
    AdjNode p, q;

    for (p = firstEdge; p; p = p->next)
    {
        for (q = p->next; q; q = q->next)
        {
            if (q->adjV < p->adjV)
            {
                temp = q->adjV;
                q->adjV = p->adjV;
                p->adjV = temp;
            }
        }
    }
}

// 判断是否是出口
bool isExit(Lake L, Vertex V)
{
    bool ret = false;

    if (V == 0)
    { // 能否从岛上跳到岸边?
        if ((DISTANCE_TO_BANK - ISLAND_RADIUS) <= L->maxJumpDist)
        {
            ret = true;
        }
    }
    else if (DISTANCE_TO_BANK - abs(L->crocodiles[V].x) <= L->maxJumpDist ||
             DISTANCE_TO_BANK - abs(L->crocodiles[V].y) <= L->maxJumpDist)
    {
        ret = true;
    }

    return ret;
}

Vertex findExit(Lake L, Vertex S, int dist[], int path[])
{
    Vertex V, ExitID = NO_EXIT;
    AdjNode W;
    dist[S] = 0;
    Queue Q = createQ(L->Nv);
    addQ(Q, S);
    while (!isEmpty(Q))
    {
        V = delQ(Q);
        // 由于是递增的遍历,所以第一个找到的出口一定是首跳最短路径出口,可以break出来
        if (isExit(L, V))
        {
            ExitID = V;
            break;
        }

        for (W = L->lake[V].firstEdge; W; W = W->next)
        {
            if (dist[W->adjV] == -1)
            {
                dist[W->adjV] = dist[V] + 1;
                path[W->adjV] = V;
                addQ(Q, W->adjV);
            }
        }
    }

    free(Q->elements);
    free(Q);

    return ExitID;
}

// 拯救007
void save007(Lake L)
{
    int dist[L->Nv], path[L->Nv];
    Vertex V;
    for (V = 0; V < L->Nv; V++)
    {
        dist[V] = path[V] = -1;
    }
    // findPath遍历寻找出口,并带出出口值
    Vertex ExitID = findExit(L, 0, dist, path);

    // 按题目要求输出
    if (ExitID == NO_EXIT)
    {
        printf("0\n");
    }
    else
    {
        showPath(L, ExitID, dist, path);
    }
}

// 利用堆栈输出具体路径
void showPath(Lake L, Vertex ExitID, int dist[], int path[])
{
    int minJumps = dist[ExitID] + 1; // 要加上最后从出口鳄鱼跳上岸的那一步
    printf("%d\n", minJumps);

    Vertex Stack[L->Nv];
    int top = -1;

    while (ExitID != -1)
    {
        top++;
        Stack[top] = ExitID;
        ExitID = path[ExitID];
    }
    top--; // 最上面存的是小岛的坐标,不需要输出
    while (top != -1)
    {
        printf("%d %d\n", L->crocodiles[Stack[top]].x, L->crocodiles[Stack[top]].y);
        top--;
    }
}

void freeGraph(Lake L)
{
    AdjNode p, q;
    Vertex V;

    for (V = 0; V < L->Nv; V++)
    {
        for (p = L->lake[V].firstEdge; p; p = q)
        {
            q = p->next;
            free(p);
        }
    }
    // L->Crocodiles和G->Graph不是通过malloc分配的空间,所以无需用free释放
    //  free(L->crocodiles);
    //  free(L->lake);
    free(L);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值