07-图5 Saving James Bond - Hard Version(浙大数据结构PTA习题)

07-图5 Saving James Bond - Hard Version

分数 30        作者 陈越        单位 浙江大学

Question:

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

代码长度限制:16 KB        时间限制:400 ms        内存限制:64 MB

题目大意:

问题大意:在一个100×100正方形的湖正中央有一个直径为15的小岛,湖中随机分布着一些鳄鱼,现在有一个人在小岛上想要通过踩鳄鱼头的方式跳到湖岸,现在需要判断此人是否能成功到达湖岸。

输入:人每次能够跳跃的最大距离、鳄鱼的总数量以及每只鳄鱼的坐标。

输出:若不能逃生到湖岸,则输出0;若能逃生到湖岸,需要找出跳跃次数最少的一条路,若存在多解,则找出第一步跳跃距离最短的那条路(题目保证唯一)。

题目解析:

该题核心为图论中的广度搜索(BFS,Breath First Search)问题,但需要在多解的情况下找出最优解。

根据题意我们可以将图中的小岛、湖中的鳄鱼以及湖岸视为图的顶点; 若在此人现在位置V1的跳跃范围内存在鳄鱼V2,那么视为V1与V2之间有边连接。

要想实现广度搜索并不困难,而难点在于如何在众多逃生路线中选出此路线中layer=1的点离小岛更近的那条路线。

因此,程序的设计思路是在进行广度搜索的过程中记录搜索的轮数,如果在某一轮搜索中发现某个顶点已经满足逃生条件,那么此时切忌直接返回结果,而是应该将这一轮的搜索进行完看是否还存在其它的逃生路径,并在此过程中记录多种逃生路径谁更满足题意的最优解。

如在上图中,有两条跳跃次数最少的逃生路径,但显然绿色那条路径layer=1的顶点距离小岛更近,即满足(minimum first jump),因此应该输出绿色那条路径。

代码展示:

# include<stdio.h>
# include<stdbool.h>
# include<stdlib.h> 
# define MaxVertemNum 101
# define MaxDistance 5001
# define Radius 7.5

// 鳄鱼的结构
typedef struct VertemNode* Vertem;
struct VertemNode{
    int X;		// 横坐标 
    int Y;		// 纵坐标 
    int Layer;	// 记录被广度搜索到的轮数 
    Vertem Parent;	  
};

// 广度搜索时存放鳄鱼的队列
typedef struct QueueNode* Queue;
struct QueueNode{
    Vertem Array[MaxVertemNum];
    int Head;
    int Rear;
};

// 存放逃生路线的堆栈
typedef struct StackNode* Stack;
struct StackNode{
    Vertem Array[MaxVertemNum];
    int Rear;
};

bool IsEscape(Vertem V, int D);
bool IsRange(Vertem V1, Vertem V2, int D);
bool IsFirstRange(Vertem V, int D);
bool IsEmptyQueue(Queue Q);
bool IsEmptyStack(Stack S);
Vertem LastVertem(Queue Q,int D,Vertem PtrArray[],int N);


int main(){
    // 接收鳄鱼的总条数与跳跃的最大距离
    int N, D;
    scanf("%d %d",&N,&D);
    // 如果能一步跳到岸,则输出结果,程序结束 
    if(D+Radius>=50){
        printf("1\n");
        return 0;
    }
	 
    // 用一个结构指针数组PtrArray来接受所有的鳄鱼
    Vertem PtrArray[N];
    int i,X,Y;
    for(i=0;i<N;i++){
        scanf("%d %d",&X,&Y);
        Vertem V = (Vertem)malloc(sizeof(struct VertemNode));
        V->X = X;
        V->Y = Y;
        V->Layer = 0;
        V->Parent = NULL;
        PtrArray[i] = V;
    }
    // 创建搜索队列,进行第一轮广度优先搜索,搜索结果依次并加入队列
    Queue Q = (Queue)malloc(sizeof(struct QueueNode));
    Q->Head = Q->Rear = -1;
    for(i=0;i<N;i++){   
        if(IsFirstRange(PtrArray[i],D)){
        	// 若在第一轮广度搜索的范围内,则将其搜索轮数设置为1 
            PtrArray[i]->Layer = 1;
            Q->Array[++Q->Rear] = PtrArray[i];
        }
    }
    
    // 寻找逃生路线,并返回逃生前踩的最后一只鳄鱼 
    Vertem LastV = LastVertem(Q,D,PtrArray,N);
    int Count = LastV->Layer;
    Stack S = (Stack)malloc(sizeof(struct StackNode));
    S->Rear = -1;
    // 能否逃生的判断 
    if(Count==-1){
        // 不能逃生
        printf("0");
    }else{
        // 能够逃生
        for(;LastV!=NULL; LastV=LastV->Parent){
            // 将结果加入堆栈方便按逃生路线输出结果 
            S->Array[++S->Rear] = LastV;
        }
        // 输出结果 
        printf("%d\n",Count+1);
        while(!IsEmptyStack(S)){
            printf("%d %d\n",S->Array[S->Rear]->X,S->Array[S->Rear]->Y);
            S->Rear--;
        }
    }
    return 0;
}

// 判读是否在第一轮广度搜索的范围内 
bool IsFirstRange(Vertem V, int D){
    int X = V->X, Y = V->Y;
    int DistanceSqure = X*X + Y*Y;
    if(DistanceSqure>(Radius+D)*(Radius+D))return false;
    else return true;
}

// 核心算法,返回逃生前的最后一只鳄鱼;若没有路径,则返回一只特殊的Layer为-1的鳄鱼
Vertem LastVertem(Queue Q,int D,Vertem PtrArray[],int N){
    Vertem V, VCopy, Res;
    // ResLayer用来记录逃生前最后一只鳄鱼的Layer,Valid作为是否成功逃生的标志 
    int i, ResLayer = 0, Valid = 0;
    int MinDistance = MaxDistance;
    while(!IsEmptyQueue(Q)){
        // 出队
        V = Q->Array[++Q->Head];
        
        // 如果成功逃生,先被急着返回,应该把这一轮判断完,看是否存在离岛更近的layer=1的鳄鱼;直到V->Layer!=ResLayer进行下一轮时才返回 
        if(Valid==1 && V->Layer != ResLayer){
            return Res;
        }
        if(IsEscape(V,D)){
            // 目的是在可能的众多路线中找出离岛更近的layer=1的鳄鱼 
            Valid = 1;
            VCopy = V;
            for(;VCopy->Parent!=NULL;VCopy=VCopy->Parent);
            if( VCopy->X * VCopy->X + VCopy->Y * VCopy->Y < MinDistance){
                MinDistance = VCopy->X * VCopy->X + VCopy->Y * VCopy->Y;
                Res = V;
                ResLayer = V->Layer;
            }
        }
        
        // 广度搜索 
        for(i=0;i<N;i++){
            if( IsRange(V,PtrArray[i],D) && PtrArray[i]->Layer==0 ){
                // 在该鳄鱼V的影响范围内且未被检索 
                PtrArray[i]->Layer = V->Layer + 1;
                PtrArray[i]->Parent = V;
                // 入队 
                Q->Array[++Q->Rear] = PtrArray[i];
            }
        }
    }
    
    // 因为在上面循环中有可能还没有return Res但又存在了Res,而队列为空即无法持续到V->Layer != ResLayer时也应返回正确结果 
    if(Valid==1){
        return Res;
    }
    // 逃生失败,返回一只Layer=-1的特殊的鳄鱼作为失败的标记 
    Vertem W = (Vertem)malloc(sizeof(struct VertemNode));
    W->Layer = -1;
    return W;
}

// 判断一个鳄鱼是否可以作为最后一个逃生点
bool IsEscape(Vertem V, int D){
    int X = V->X;
    int Y = V->Y;
    if(X+D>=50 || X-D<=-50 || Y+D>=50 || Y-D <= -50)return true;
    else return false;
}

// 判读队列是否为空
bool IsEmptyQueue(Queue Q){
    if(Q->Head == Q->Rear)return true;
    else return false;
}

// 判读堆栈是否为空
bool IsEmptyStack(Stack S){
    if(S->Rear == -1)return true;
    else return false;
}

// 判读V2是否在V1的辐射范围之内 
bool IsRange(Vertem V1, Vertem V2, int D){
    int X1 = V1->X, Y1 = V1->Y;
    int X2 = V2->X, Y2 = V2->Y;
    int DistanceSqure = (X1-X2)*(X1-X2) + (Y1-Y2)*(Y1-Y2);
    if(DistanceSqure>D*D)return false;
    else return true;
}

运行结果: 

  • 26
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值