07-图5 Saving James Bond - Hard Version (30分)

16 篇文章 0 订阅
3 篇文章 0 订阅

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.
假设湖是一个100乘100的正方形。 假设湖的中心在(0,0),东北角在(50,50)。 中央岛是直径为15,以(0,0)为中心的圆盘。许多鳄鱼在湖的不同位置。考虑到每个鳄鱼的坐标和詹姆斯可以跳跃的距离,你必须告诉他到达银行的最短路径。 路径的长度是詹姆斯做出的跳跃数。
Input Specification:

Each input file contains one test case. Each case starts with a line containing two positive integers NN (\le 100≤100), the number of crocodiles, and DD, the maximum distance that James could jump. Then NN lines follow, each containing the (x, y)(x,y) location of a crocodile. Note that no two crocodiles are staying at the same position.
每个输入文件包含一个测试用例。 每个案例从一个包含两个正整数的行开始,鳄鱼的数量N(≤100)和詹姆斯可以跳跃的最大距离D。 接下来的N行,每个包含鳄鱼的(x,y)位置。 注意,没有两个鳄鱼停留在相同的位置。
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)(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.
对于每个测试用例,如果詹姆斯可以逃脱,在一行输出他必须做的最小跳跃数。 然后从下一行开始,在每一行中输出从岛到银行上的每对鳄鱼在路径上的位置(x,y)。 如果詹姆斯不可能逃脱,跳跃的数给出0。 如果有许多最短路径,只输出具有最小第一跳的那一个,这保证是唯一的。

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

思路:

因为才学了最短路生成算法,因此这道题目我首先反应就是使用Floyd法解决,debug总是痛苦的,最后我还是用printf大法,把每个阶段中的每一个int、每一个数组和每一个矩阵全都打印出来,手动计算并核对,挨个排查,最终确定了自己的错误:思路是对的,锅全是他妈的粗心造成的。以后遇到相似的代码,一定一定要核对再三。一个小于号换成大于号,一个矩阵写成另一个矩阵,就让我花了一天的时间debug。
方法一:
使用Floyd算法。对于这道题目,生成的图有三种特殊的点(鳄鱼):起点(第一条鳄鱼),过程点(中间的鳄鱼)和终点(最后一条鳄鱼)。起点因为有小岛半径的加成,因此Jond最大可以达到的距离为D+7.5;过程点最大距离为跳跃距离D;终点只要距离岸边的距离小于跳跃距离D即可。
因此,在表达距离的矩阵G和表达路径的矩阵Path外,我还建立了两个数组:start数组存储起点,end数组存储终点。起点终点都可以轻易算出,而过程点就要用Floyd法求出。存储的数据从数组下标1开始。
在解题的过程中,还不要忘记特殊情况:若有鳄鱼在岛上或在岸上;若邦德体力惊人,直接从岛跳到岸上去。还有就是如果跳跃的次数一样的情况出现,输出的是第一次跳的鳄鱼距离小岛最近的方案。因为忽略了这种情况,导致我一个测试点总是无法通过。最后再看别人的BFS法求解的代码时才醒悟。
首先给出sort的用法。

#include <algorithm>

using namespace stdbool compare(int a,int b)
{
      return a>b;   //降序排列,如果改为return a<b,则为升序(如果使用sort不写compare函数,则默认升序) 

}

int main()
{
 int a[20]={2,4,1,23,5,76,0,43,24,65},i;
 for(i=0;i<20;i++)
  cout<<a[i]<<endl;

 sort(a,a+20);                      //升序排列a数组中的从下标0到19的函数; 
 for(i=0;i<20;i++)
 cout<<a[i]<<endl;

 sort(a+1,a+19,compare);            //降序排列a数组中的从下标1到18的函数; 
 for(i=0;i<20;i++)
 cout<<a[i]<<endl;
 return 0;
}

接下来是本题的代码。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
using namespace std;

#define MaxNum 101 
#define INFINITY 65535

struct {            //存储鳄鱼坐标 
    int x;
    int y;
}Position[MaxNum];

typedef struct {            //图的结构 
    int Nv;
    int Ne;
    int G[MaxNum][MaxNum]; 
    int Path[MaxNum][MaxNum];
}GNode;
typedef GNode *MGraph; 


double Distance(int i,int j)//计算两个鳄鱼的距离   
{  
    double b;  
    b=sqrt(pow(Position[i].x-Position[j].x,2)+pow(Position[i].y-Position[j].y,2));  
    return b;  
}

bool cmp(int i,int j)       //排序函数中的比较函数。把第一次能跳上的鳄鱼按照近小岛的距离升序排列 
{
    return Distance(i,0) < Distance(j,0);
}

bool CanEscape(int i,int D)//判断是否能从这个鳄鱼直接跳到岸上 
{
    if((Position[i].x+D>=50)||(Position[i].y+D>=50)||(Position[i].x-D<=-50)||(Position[i].y-D<=-50))
    {
        return true;
    }else
    {
        return false;
    }
}

int CountStep(MGraph Graph,int i,int j)//计算所需要的步数 
{
    int k,count=1;      //默认为1,是从小岛跳到鳄鱼上的一步 
    while(i!=j)
    {
        k=Graph->Path[i][j];
        i=k;
        count++;
    }
    count++;            //再加1,从鳄鱼跳到岸上 
    return count;
}

void CoutWay(MGraph Graph,int i,int j)  //打印出经过的鳄鱼的坐标 
{
    printf("%d %d\n",Position[i].x,Position[i].y);
    int k;
    while(i!=j)
    {
        k=Graph->Path[i][j];
        printf("%d %d\n",Position[k].x,Position[k].y);
        i=k;
    }
}



int main()
{
    int N,D;                        //鳄鱼数量和跳跃距离
    MGraph Graph=(MGraph)malloc(sizeof(GNode));
    for(int i=0;i<MaxNum;i++)       //初始化G和Path 
    {
        for(int j=0;j<MaxNum;j++)
        {
            Graph->G[i][j]=INFINITY;
            Graph->Path[i][j]=j;
        }
    }

    scanf("%d %d",&N,&D);   
    if(D>=42.5)             //当007属蚂蚱的时候,能从岛上直接挑到岸上 
    {
        printf("1");
        return 0;
    }
    Position[0].x=0;        
    Position[0].y=0;    
    for(int i=1;i<=N;i++)   //数组下标1开始存储数据 
    {
        scanf("%d %d",&Position[i].x,&Position[i].y);
        if((Distance(i,0)<=7.5)||(Position[i].x>=50)||(Position[i].y>=50)||(Position[i].x<=-50)||(Position[i].y<=-50)) 
        {   //当鳄鱼在岸上或在小岛上时,这条鳄鱼可以忽略不计。因此鳄鱼数量N减一,下条鳄鱼顶替它的数组中的位置 
            i--;
            N--;
        }   
    }

    //以上为输入部分 
    for(int i=1;i<=N;i++)
    {
        for(int j=1;j<=N;j++)
        {
            if(Distance(i,j)<=D)
            {
                if(i==j)
                {   //从某条鳄鱼到它自己设为不通路
                    Graph->G[i][j]=0;
                    Graph->G[i][j]=0;
                }else{
                    Graph->G[i][j]=1;
                    Graph->G[j][i]=1;                   
                }               
            }
        }   
    }


    int start[MaxNum],startnum=1;//存储起点鳄鱼的数组和起点数 
    int end[MaxNum],endnum=1;    //存储终点鳄鱼的数组和终点数
    for(int i=0;i<MaxNum;i++)   //初始化 
    {
        start[i]=-1;
        end[i]=-1;
    }

    for(int i=1;i<=N;i++)       //起点的Position下标放入数组 
    {
        if(Distance(i,0)-7.5<=D)
        {
            start[startnum++]=i;
        }
    }



    for(int i=1;i<=N;i++)       //终点的Position下标放入数组 
    {
        if(CanEscape(i,D))
        {
            end[endnum++]=i; 
        }
    }
    startnum--;
    endnum--;

    sort(start+1,start+startnum+1,cmp);//把第一次可以跳上去的鳄鱼按离小岛的距离升序排列 


    for(int k=1;k<=N;k++)       //核心,Floyd算法 
    {
        for(int i=1;i<=N;i++)
        {
            for(int j=1;j<=N;j++)
            {
                if(Graph->G[i][j]>Graph->G[i][k]+Graph->G[k][j])
                {
                    Graph->G[i][j]=1;
                    Graph->Path[i][j]=Graph->Path[i][k];
                }
            }
        }
    }

    int minstep=INFINITY,first=0,last=0;    //分别为最小的跳跃数,存储起点下标,存储终点下标 
    for(int i=1;i<=startnum;i++)
    {
        for(int j=1;j<=endnum;j++)
        {
            if(minstep>CountStep(Graph,start[i],end[j])&&(Graph->G[start[i]][end[j]]==1))
            {
                minstep=CountStep(Graph,start[i],end[j]);
                first=start[i];
                last=end[j];
            }
        }
    }
    if(minstep!=INFINITY)       
    {
        printf("%d\n",minstep);
        CoutWay(Graph,first,last);
    }else{  //当最小的跳跃数为65535,就是不可能逃脱 
        printf("0");
    }


    return 0;
}

方法二:和Easy Version一样,方法二也是用BFS遍历来求解。这里的思路和我之前做的六度空间的题目类似:建立两个整型,last和tail,分别用于记录当前一层的最后一个元素和下一层的最后一个元素。当出队的元素和last相等时,就意味着将进入下一层。此时last更新为tail,tail不断更新至当前最新入队的元素。
同方法一一样,要确定可以从哪些鳄鱼起跳。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
#include <queue> 
#include <stack>
using namespace std;

#define MaxNum 101 
#define INFINITY 65535

bool Visited[MaxNum]={false};
int Path[MaxNum]={-1};
bool answer=false;  //判断能否 
struct {            //存储鳄鱼坐标 
    int x;
    int y;
}Position[MaxNum];

double Distance(int i,int j)//计算两个鳄鱼的距离   
{  
    double b;  
    b=sqrt(pow(Position[i].x-Position[j].x,2)+pow(Position[i].y-Position[j].y,2));  
    return b;  
}

bool cmp(int i,int j)       //排序函数中的比较函数。把第一次能跳上的鳄鱼按照近小岛的距离升序排列 
{
    return Distance(i,0) < Distance(j,0);
}

bool CanEscape(int i,int D)//判断是否能从这个鳄鱼直接跳到岸上 
{
    if((Position[i].x+D>=50)||(Position[i].y+D>=50)||(Position[i].x-D<=-50)||(Position[i].y-D<=-50))
    {
        return true;
    }else
    {
        return false;
    }
}


int main()
{
    int N,D;                //鳄鱼数量和跳跃距离 
    scanf("%d %d",&N,&D);   
    if(D>=42.5)             //当007属蚂蚱的时候,能从岛上直接挑到岸上 
    {
        printf("1");
        return 0;
    }
    Position[0].x=0;        
    Position[0].y=0;    
    for(int i=1;i<=N;i++)   //数组下标1开始存储数据 
    {
        scanf("%d %d",&Position[i].x,&Position[i].y);
        if((Distance(i,0)<=7.5)||(Position[i].x>=50)||(Position[i].y>=50)||(Position[i].x<=-50)||(Position[i].y<=-50)) 
        {//当鳄鱼在岸上或在小岛上时,这条鳄鱼可以忽略不计。因此鳄鱼数量N减一,下条鳄鱼顶替它的数组中的位置 
            i--;
            N--;
        }   
    }
    //以上为输入部分   
    int start[MaxNum],startnum=1;//存储起点鳄鱼的数组和起点数 
    for(int i=0;i<MaxNum;i++)   //初始化 
    {
        start[i]=-1;
    }

    for(int i=1;i<=N;i++)       //起点的Position下标放入数组 
    {
        if(Distance(i,0)-7.5<=D)
        {
            start[startnum++]=i;
        }
    }
    startnum--;

    sort(start+1,start+startnum+1,cmp);//把第一次可以跳上去的鳄鱼按离小岛的距离升序排列 

    queue<int> q;
    int last,tail; 
    for(int i=1;i<=startnum;i++)
    {       //第一层(第一次可以跳上去的鳄鱼)入队 
        q.push(start[i]);
        Visited[start[i]]=true;
        last=start[i];
    }
    int ministep=2;//跳一次鳄鱼跳一次岸,起码跳2次
    while(!q.empty())
    {
        int p=q.front();
        q.pop();
        if(CanEscape(p,D))//当满足条件时,因为顺序是颠倒的,故把符合条件的下标压入栈 
        {
            stack<int> s;
            printf("%d\n",ministep);
            for(int i=0;i<ministep-1;i++)
            {
                s.push(p);
                p=Path[p];
            }
            while(!s.empty())
            {
                p=s.top();
                printf("%d %d\n",Position[p].x,Position[p].y);
                s.pop();
            }
            answer=true;
            break;
        }else
        {   
            for(int i=1;i<=N;i++)
            {    
                if(Visited[i]==false&&Distance(p,i)<=D)
                {   //没跳过而且能跳上去
                    q.push(i); 
                    Visited[i]=true;
                    Path[i]=p;//进队鳄鱼的上一条鳄鱼
                    tail=i;   //更新tail,在当前for循环结束后会指向下一层能跳上去的最后一只鳄鱼 
                }
            }
            if(last==p)//出队的元素与last相等,说明本层的点都已经将进入下一层  
            {
                ministep++;
                last=tail;
            } 
        }       
    } 
    if(answer==false)
    {
        printf("0");
    }
    return 0;
}

最后,记录自己对BFS的认知。依次分为三块:
1.完成条件。当发现最短路径的时候,要输出Path数组中符合条件的路径。因为是颠倒的,因此需要把路径压入栈,然后pop输出;
2.执行步骤。遍历下一层的每个结点。当该结点没有Visited过而且符合条件,压入队列;
3.记录层数。当出队列的数就是当前层数最后一个结点的下标时,说明要进入下一层了。因此last=tail。
核心代码如下:

    while(!q.empty())
    {
        int p=q.front();
        q.pop();
        if(CanEscape(p,D))//1.完成条件:当满足条件时,因为顺序是颠倒的,故把符合条件的下标压入栈 
        {
            stack<int> s;
            printf("%d\n",ministep);
            for(int i=0;i<ministep-1;i++)
            {
                s.push(p);
                p=Path[p];
            }
            while(!s.empty())
            {
                p=s.top();
                printf("%d %d\n",Position[p].x,Position[p].y);
                s.pop();
            }
            answer=true;
            break;
        }else
        {   
            for(int i=1;i<=N;i++)
            {    2.执行步骤:符合条件且没有访问过的结点压入队列
                if(Visited[i]==false&&Distance(p,i)<=D)
                {   //没跳过而且能跳上去
                    q.push(i); 
                    Visited[i]=true;
                    Path[i]=p;//进队鳄鱼的上一条鳄鱼
                    tail=i;   //更新tail,在当前for循环结束后会指向下一层能跳上去的最后一只鳄鱼 
                }
            }
            if(last==p)//3.记录层数:出队的元素与last相等,说明本层的点都已经将进入下一层  
            {
                ministep++;
                last=tail;
            } 
        }       
    } 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值