7-1 拯救007(基于图的遍历考点)

11 篇文章 0 订阅
10 篇文章 1 订阅

在老电影“007之生死关头”(Live and Let Die)中有一个情节,007被毒贩抓到一个鳄鱼池中心的小岛上,他用了一种极为大胆的方法逃脱 —— 直接踩着池子里一系列鳄鱼的大脑袋跳上岸去!(据说当年替身演员被最后一条鳄鱼咬住了脚,幸好穿的是特别加厚的靴子才逃过一劫。)

设鳄鱼池是长宽为100米的方形,中心坐标为 (0, 0),且东北角坐标为 (50, 50)。池心岛是以 (0, 0) 为圆心、直径15米的圆。给定池中分布的鳄鱼的坐标、以及007一次能跳跃的最大距离,你需要告诉他是否有可能逃出生天。

输入格式:

首先第一行给出两个正整数:鳄鱼数量 N(≤100)和007一次能跳跃的最大距离 D。随后 N 行,每行给出一条鳄鱼的 (x,y) 坐标。注意:不会有两条鳄鱼待在同一个点上。

输出格式:

如果007有可能逃脱,就在一行中输出"Yes",否则输出"No"。

输入样例 1:

14 20
25 -15
-25 28
8 49
29 15
-35 -2
5 28
27 -29
-8 -28
-20 -35
-25 -20
-13 29
-30 15
-35 40
12 12

输出样例 1:

Yes

输入样例 2:

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

输出样例 2:

No

1.做该题时首先要明确要用数据结构:(这样不仅能够方便我们解题同时能够减少时间的复杂度)

①:理解题目以及考点:题目本质上就是基于图的遍历。

②:一开始我们画出关于人在鳄鱼中跳的草图方便我们理解题目,因此既然我们是用草图进行理解的,则想到图的数据结构进行解题,这就是为什么我要用图的数据结构进行解题的原因。

2.明确我所用的数据结构需要的东西或需要理解的算法:

①:在用图的算法时要理解两个东西:一个是顶点,另一个是边。

图的顶点和边是一个抽象的概念,并不是一个具体的概念。因此图的顶点要理解为有某种属性的对象(即并不一定是一个点才是顶点)。边理解为对象之间的关系。

②:在用图的算法时一定要确定顶点和边分别是什么。边和顶点都可以不是唯一的,即只要有某种属性就可以表示为顶点(图中的顶点并不是存的是只有一种属性的对象可以是多种),同时边也同上,可以表示为不同对象之间的关系。

这里的顶点是:鳄鱼,岛,岸边。这里的边是:能从一个鳄鱼跳到另一个鳄鱼,一开始(从岛开始)能跳到的鳄鱼,鳄鱼可以跳到岸边的。

③:如何表示一个图:在这道题中我采用二维矩阵表示的是边的关系。

3.涉及的算法以及思路解题步骤

①:先将能够从岛(抽象成数据0表示)开始跳到的鳄鱼(鳄鱼顶点抽象成1~N的数)先建立建立边的关系。

人一开始可以跳到的鳄鱼判断条件为:从(0,0)(起点)到鳄鱼坐标的距离小于岛的半径加上人可以跳的距离的值就是一开始可以跳到的鳄鱼。

②:再建立人在某一个鳄鱼上可以跳到另一个鳄鱼的关系。

这时人可以跳到的范围就不需要加上岛的半径。

③:我采用的是深度优先遍历的方式进行遍历:

实参为:图,当前所在的结点,人可以跳的距离。从主函数进入是开始的位置是0。

0行存的是一开始可以跳到的鳄鱼。

每次在一个位置的时候就进行判断可不可以从该位置直接跳到岸边。如果可以则在输出YES后再用exit(0)强制结束程序(return也可以)。如果不可以则用标记数组进行标记,表示从该位置的鳄鱼不可以跳到岸边,然后再找该鳄鱼的相邻鳄鱼(这里就是深度优先遍历的主要程序了)。

④:并不需要走到最后的顶点再进行判断能不能跳,而是每跳到一个鳄鱼身上就进行判断。

我的做题过程:

第一次:公式错误(两点之间的距离不小心做成加法)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX 150

//边的结构
struct ENode {
	int V1,V2;
};
typedef struct ENode *bian;

//临界顶点的结构
struct AdjVNode {
	int subscript;
	struct AdjVNode *next_subscript;
};
typedef struct AdjVNode *spot;

//邻接表的表头结点
typedef struct headAdjVNode {
	int head_spot;
	spot next_spot;
} H[MAX];

//图的结构:采用邻接表的方式
struct GNode {
	int Nv;//顶点数
	int Ne;//边数
	H G;
};
typedef struct GNode *list;

//一维数组存储鳄鱼的结构
struct ey {
	int x,y;
};
typedef struct ey eryu;
eryu zoubiao[MAX];

//访问标记
int visit[MAX];

list creat(int sum);
void gojian(list head,int num,int sum);
void charu(list head,int left,int right);
void bianli(list head,int now_spot,int num); 

int main() {
	int sum,num;
	//一开始的岛的半径维7.5
	scanf("%d%d",&sum,&num);
	list tu;
	//初始化
	tu=creat(sum);
	//进行构造邻接表;即边的构造
	gojian(tu,num,sum);
//	for(int i=0;i<=sum;i++){
//		printf("%d:",i);
//		for(spot tran=tu->G[i].next_spot;tran;tran=tran->next_subscript)
//		printf(" %d",tran->subscript);
//		printf("\n");
//	}
	//进行开始遍历程序-->和单纯的遍历还是有很多的不同的
	if(tu->G[0].next_spot==NULL) { //第一次就是空的表示岛的周围没有可以踩的
		printf("No\n");
	}else if(num+7.5>=50){
		printf("Yes\n");
		printf("PPP");
	}
	else {
		bianli(tu,0,num);
		printf("No\n");
	}
	return 0;
}
//初始化图:即建立一个没有边的图
list creat(int sum) {
	list head;
	head=(list)malloc(sizeof(struct GNode));
	head->Nv=sum;
	head->Ne=0;
	for(int i=0; i<=sum; i++) {
		head->G[i].head_spot=i;
		visit[i]=0;//初始标记为0
		head->G[i].next_spot=NULL;
	}

	return head;
}
//图的构建 :即建立能够相互跳的两个顶点
void gojian(list head,int num,int sum) {
	//存进一维数组中,即存顶点
	zoubiao[0].x=0,zoubiao[0].y=0;
	//将顶点存入一维数组
	for(int i=1; i<=sum; i++)
		scanf("%d%d",&zoubiao[i].x,&zoubiao[i].y);
	//先存0.0的相邻的边,因为第一次的半径不一样(距离不一样)一开始的跳跃距离是7.5+num
	for(int i=1; i<=sum; i++) {
		if(sqrt(pow(zoubiao[0].x-zoubiao[i].x,2)-pow(zoubiao[0].y-zoubiao[i].y,2))<=7.5+num)
			charu(head,0,i);
	}
	//进行其他坐标的判断 跳跃的距离不用小岛,就是num
	for(int i=1; i<sum; i++) {
		for(int j=i+1; j<=sum; j++) {
			if(sqrt(pow(zoubiao[i].x-zoubiao[j].x,2)-pow(zoubiao[i].y-zoubiao[j].y,2))<=num)
				charu(head,i,j);
		}
	}
}
//创建边
void charu(list head,int left,int right) {
	bian tran;
	spot spot_tran;
	spot_tran=(spot)malloc(sizeof(struct AdjVNode));
	tran=(bian)malloc(sizeof(struct ENode));
	tran->V1=left;
	tran->V2=right;
	//注意采用的是头插法
	//先建左边的
	spot_tran->subscript=right;
	spot_tran->next_subscript=head->G[left].next_spot;
	head->G[left].next_spot=spot_tran;
	//再插右边
	spot_tran=(spot)malloc(sizeof(struct AdjVNode));
	spot_tran->subscript=left;
	spot_tran->next_subscript=head->G[right].next_spot;
	head->G[right].next_spot=spot_tran;
}
//进行开始遍历程序,采用dbf的遍历的方法--》还是需要进行标记的->不会进行重复的判断

void bianli(list head,int now_spot,int num) {
     //now_spot表示当前位置的下标
	//进行判断是不是可以跳,如果可以跳就不用再进行访问->这一步就先当于在原来方式中的输出
	//判断该点到岸上的距离exit(0)执行强制结束
	//如何判断到边界的距离:利用点绝对值转为第一象限的在相减
	if(50-abs(zoubiao[now_spot].x)<=num || 50-abs(zoubiao[now_spot].y)<=num){
		//第一次的判断和这个不一样 
		printf("Yes\n");
		exit(0);
	} 
//	跳不上岸则进行标记,这里的标记的含义也表示这个鱼不能人跳上岸 
	visit[now_spot]=1;
	for(spot tran=head->G[now_spot].next_spot;tran;tran=tran->next_subscript){
		if(visit[tran->subscript]==0)
		bianli(head,tran->subscript,num);
	}
}

改进:正确

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX 150

//边的结构
struct ENode {
	int V1,V2;
};
typedef struct ENode *bian;

//临界顶点的结构
struct AdjVNode {
	int subscript;
	struct AdjVNode *next_subscript;
};
typedef struct AdjVNode *spot;

//邻接表的表头结点
typedef struct headAdjVNode {
	int head_spot;
	spot next_spot;
} H[MAX];

//图的结构:采用邻接表的方式
struct GNode {
	int Nv;//顶点数
	int Ne;//边数
	H G;
};
typedef struct GNode *list;

//一维数组存储鳄鱼的结构
struct ey {
	int x,y;
};
typedef struct ey eryu;
eryu zoubiao[MAX];

//访问标记
int visit[MAX];

list creat(int sum);
void gojian(list head,int num,int sum);
void charu(list head,int left,int right);
void bianli(list head,int now_spot,int num); 

int main() {
	int sum,num;
	//一开始的岛的半径维7.5
	scanf("%d%d",&sum,&num);
	list tu;
	//初始化
	tu=creat(sum);
	//进行构造邻接表;即边的构造
	gojian(tu,num,sum);
//	for(int i=0;i<=sum;i++){
//		printf("%d:",i);
//		for(spot tran=tu->G[i].next_spot;tran;tran=tran->next_subscript)
//		printf(" %d",tran->subscript);
//		printf("\n");
//	}
	//进行开始遍历程序-->和单纯的遍历还是有很多的不同的
	if(tu->G[0].next_spot==NULL) { //第一次就是空的表示岛的周围没有可以踩的
		printf("No\n");
	}else if(num+7.5>=50){//这个判断有没有都无所谓,这只是进行判断写的
		printf("Yes\n");
	}
	else {
		bianli(tu,0,num);
		printf("No\n");
	}
	return 0;
}
//初始化图:即建立一个没有边的图
list creat(int sum) {
	list head;
	head=(list)malloc(sizeof(struct GNode));
	head->Nv=sum;
	head->Ne=0;
	for(int i=0; i<=sum; i++) {
		head->G[i].head_spot=i;
		visit[i]=0;//初始标记为0
		head->G[i].next_spot=NULL;
	}

	return head;
}
//图的构建 :即建立能够相互跳的两个顶点
void gojian(list head,int num,int sum) {
	//存进一维数组中,即存顶点
	zoubiao[0].x=0,zoubiao[0].y=0;
	//将顶点存入一维数组
	for(int i=1; i<=sum; i++)
		scanf("%d%d",&zoubiao[i].x,&zoubiao[i].y);
	//先存0.0的相邻的边,因为第一次的半径不一样(距离不一样)一开始的跳跃距离是7.5+num
	for(int i=1; i<=sum; i++) {
		int goudu=sqrt(pow(zoubiao[i].x,2)+pow(zoubiao[i].y,2)); 
		if(goudu<=(7.5+num))
			charu(head,0,i);
	}
	//进行其他坐标的判断 跳跃的距离不用小岛,就是num
	for(int i=1; i<sum; i++) {
		for(int j=i+1; j<=sum; j++) {
			if(sqrt(pow((zoubiao[i].x-zoubiao[j].x),2)+pow((zoubiao[i].y-zoubiao[j].y),2))<=num)
				charu(head,i,j);
		}
	}
}
//创建边
void charu(list head,int left,int right) {
	bian tran;
	spot spot_tran;
	spot_tran=(spot)malloc(sizeof(struct AdjVNode));
	tran=(bian)malloc(sizeof(struct ENode));
	tran->V1=left;
	tran->V2=right;
	//注意采用的是头插法
	//先建左边的
	spot_tran->subscript=right;
	spot_tran->next_subscript=head->G[left].next_spot;
	head->G[left].next_spot=spot_tran;
	//再插右边
	spot_tran=(spot)malloc(sizeof(struct AdjVNode));
	spot_tran->subscript=left;
	spot_tran->next_subscript=head->G[right].next_spot;
	head->G[right].next_spot=spot_tran;
}
//进行开始遍历程序,采用dbf的遍历的方法--》还是需要进行标记的->不会进行重复的判断

void bianli(list head,int now_spot,int num) {
     //now_spot表示当前位置的下标
	//进行判断是不是可以跳,如果可以跳就不用再进行访问->这一步就先当于在原来方式中的输出
	//判断该点到岸上的距离exit(0)执行强制结束
	//如何判断到边界的距离:利用点绝对值转为第一象限的在相减
	if(50-abs(zoubiao[now_spot].x)<=num || 50-abs(zoubiao[now_spot].y)<=num){
		//第一次的判断和这个不一样 
		printf("Yes\n");
		exit(0);
	} 
//	跳不上岸则进行标记,这里的标记的含义也表示这个鱼不能人跳上岸 
	visit[now_spot]=1;
	for(spot tran=head->G[now_spot].next_spot;tran;tran=tran->next_subscript){
		if(visit[tran->subscript]==0)
		bianli(head,tran->subscript,num);
	}
}

 其他人的方法:

思路都差不多,他们是已经在基于图的理解,再进行程序的改进。

#include"stdio.h"
#include"string.h"
#include"algorithm"
   using namespace std;
typedef struct Node
{
    int x,y;
}Node;
int N,M;
Node node[1000];
int vis[1000];
int flag=0;
//从岛跳到第i号鳄鱼是否可行
int frist(int i)
{
    int p1=node[i].x*node[i].x;
    int p2=node[i].y*node[i].y;
    int m=(M+7.5)*(M+7.5);
    if(p1+p2<=m)
        return 1;
    return 0;
}
//求第i号和第j号鳄鱼之间的距离,并判断
int jump(int i,int j)
{
    int p1=(node[i].x-node[j].x)*(node[i].x-node[j].x);
    int p2=(node[i].y-node[j].y)*(node[i].y-node[j].y);
    //圆的公式
if(p1+p2<=M*M)
        return 1 ;
    return 0;
}
//求当前位置能否直接跳到岸边
int key(int k)
{
    if(node[k].x-M<=-50||node[k].x+M>=50||node[k].y-M<=-50||node[k].y+M>=50)
        return 1;
    return 0;
}
void dfs(int k)
{
    vis[k]=1;
    if(key(k))
    {
        flag=1;return ;
    }
    for(int i=0;i<N;i++)
    {
        if(!vis[i]&&jump(k,i))
        {
            dfs(i);
        }
    }
}
int main()
{
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&N,&M);
    for(int i=0;i<N;i++)
        scanf("%d%d",&node[i].x,&node[i].y);
    if(M>=50)
    {
        printf("Yes\n");return 0;
    }
    for(int i=0;i<N;i++)
    {
        if(!vis[i]&&frist(i))//表示第一个跳第i号鳄鱼
        {
            dfs(i);
        }
    }
    if(flag==1)
        printf("Yes\n");
    else
        printf("No\n");
}

  • 10
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小丞啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值