山头狙击战(C++,二分法,map)

题目描述

小明为了掩护大部队,单枪匹马同敌人周旋,后来被敌人包围在某山头……等等,为什么怎么听怎么像狼牙山五壮士!不过不用着急,这次小明携带了足够的弹药,完全可以将涌上来的敌人一个一个干掉。小明是个神枪手,只要他的枪膛中有子弹,他就能将在他射程m(用从敌人位置到山头的直线距离算)以内的一个敌人瞬间射杀。但如果在射程内没有敌人,出于节约子弹考虑和面子问题,小明会等待敌人靠近然后射击。
正当小明为自己的强大而自我膨胀时,他忽然发现了一个致命的失误:他携带的枪是单发枪,每射出一发子弹都必须花k秒钟的时间装子弹。而凶残的敌人才不会花时间等你换子弹呢。他们始终在以1m/s的速度接近山头。而如果在一个敌人到达山头时小明无法将他击毙,那么我们可怜的小明就将牺牲在敌人的刺刀下。现在小明用心灵感应向你发出求助:要保住自己的性命并且歼灭所有敌人,小明最多只能用多少时间给枪装上一发子弹?
说明:假设一开始小明的枪中就有一发子弹,并且一旦确定一个装弹时间,小明始终会用这个时间完成子弹的装卸。希望你能帮助小明脱离险境。

输入格式

每组输入数据,第一行有两个整数n和m,(2≤n≤100,000; 1≤m≤10,000,000)n代表敌人个数,m代表小明的射程。
接下来有n行,每行一个整数mi,(1≤mi≤10,000,000),代表每个敌人一开始相对山头的距离(单位为米)。

输出格式

每组输出数据仅有一个整数,代表小明的换弹时间(单位为秒)。

样例输入

6 100
236
120
120
120
120
120

样例输出

25

解题思路:

一看到关于换弹时间的要求:在尽量长的同时不能超过一个值,就可以想到用二分法

考虑到题中并未说明输入的数据是有序的,我们需要输入数据进行排序

那么采用什么方法排序呢,是输入和排序同时进行?还是利用数组加sort()函数?

暂时无法确定

那么首先,二分法的实现如下

int left = 0, right = int(1e7) + 1, middle;
while (left + 1 != right)
{
	middle = (left + right) / 2;
	if (is_die(middle))//小明成功存活,判断换弹时间是否能够更长
	{
		max_time = middle;
		left = middle;
	}
	else//小明被做掉了,换弹应该快一些
	{
		right = middle;
	}
}

然后剩下的问题就是实现一个函数用于middle的判断了

参数列表:唯一需要传入的参数即为middle也就是换弹时间

返回值类型:需要返回bool类型值,因为返回值的作用只是判断接下来left = middle还是right = middle

本题的核心在于函数体的实现

先理解题意,什么样的换弹时间符合条件

换弹时间应该让小明能够存活下来,那么怎么判断小明是否能够存活

首先我们会建立这样一个模型,小明坐标始终为0,所有敌人不断前进,判断最近的敌人是否能够杀死小明

我们发现了,这里也同样要求敌人的位置是有序的,我们同样之后再说明

如何实现所有敌人不断前进?去改变所有敌人的坐标显然不是一个明智的选择

那么我们先说明第一种方法

用一个变量去累计时间,如果不是最近的敌人,先让他不要动,等轮到他了,他就直接瞬移一段距离

这种方法可行,但这里不予以实现,我们接下来要说明第二种方法

问题要抓住本质,本质上就是小明和敌人之间的距离不断变小的过程

那么换弹时间就可以转化为距离的变化,同样,不需要改变敌人的位置,我们可以改变小明的位置

那么现在题意完成了转化:

现在有一堆静止的靶子,小明每走一段距离才能开一枪

那么现在进一步考虑如何判断小明是否会遇到靶子的问题

为了避免TLE,我们首先考虑,能否一次性计算小明能否存活?

射程就是一个问题,那么再进一步,能否一次性计算射程内的所有敌人所需要的最短换弹时间?

如果1个敌人在int(1e7)处,int(1e5)-1个敌人就在1处,显然我们不需要考虑最远的那个敌人,只要能干掉面前的敌人,小明就能够成功存活

那么就只能采用遍历的方式了,我们现在来说明循环的实现

循环结束条件:当然就是敌人全部被干掉或者小明被干掉

然后说明循环体,我们需要考虑,射程内有没有敌人(当然这时候枪里有子弹,否则有没有敌人都是无意义的)

没有敌人的话,我们需要继续前进,直到有敌人进入射程

有敌人的话,我们需要判断小明能否存活

到这里了我们一定知道如何判断:小明干掉所有敌人需要前进的距离是否小于小明与敌人间的距离(为什么相等时小明能够存活呢,因为题中说小明能够瞬间干掉敌人)

不小于的话,那么由于换弹太慢,小明被干掉了

小于,敌人被干掉,我们继续下一轮循环

至此,函数实现完毕,以下是函数功能的实现

bool is_die(int bullet_time)//判断小明是否会die
{
	//遍历enemy_map
	int xm_index = 0;//小明的位置(注:每轮都需要初始化)
	map<int, int>::iterator step = enemy_map.begin();
	while (step != enemy_map.end())//干掉每一个敌人
	{
		if (step->first - xm_index > shoot_distance)//如果射程内没有敌人
		{
			xm_index += step->first - xm_index - shoot_distance;//向前进,直到敌人进入射程
		}
		else//如果射程内有敌人
		{
			if (step->first - xm_index < 0)//哦,原来敌人已经把我干掉了
			{
				//出现这种情况是因为在确认存活并换弹的过程中,有敌人把小明干掉了
				return false;
			}
			if ((step->second - 1) * bullet_time <= step->first - xm_index)//如果小明不能走到敌人的面前
			{
				//注意在判断时,不用考虑杀死最后一个敌人后的换弹时间
				xm_index += step->second * bullet_time;//确认存活,再换弹
				step++;
			}
			else//如果小明会die
			{
				return false;
			}
		}
	}
	//循环退出,小明存活
	return true;
}

到了这里,我想已经不用我去说怎么排序了,但还是做一下说明

排序的要求就是按照位置排序,每个位置要带有敌人数量的记录

其实就是存储并排序一对数据,那么很容易想到借助STL中的map

key存储位置,自动排序

value存储敌人数量

至此,本题实现完成

本题完整的代码如下

#include <iostream>
#include <map>
using namespace std;

map<int, int> enemy_map;//key用于存储敌人位置,value用于存储该位置的敌人数量
int shoot_distance;//射程

bool is_die(int bullet_time)//判断小明是否会die
{
	//遍历enemy_map
	int xm_index = 0;//小明的位置(注:每轮都需要初始化)
	map<int, int>::iterator step = enemy_map.begin();
	while (step != enemy_map.end())//干掉每一个敌人
	{
		if (step->first - xm_index > shoot_distance)//如果射程内没有敌人
		{
			xm_index += step->first - xm_index - shoot_distance;//向前进,直到敌人进入射程
		}
		else//如果射程内有敌人
		{
			if (step->first - xm_index < 0)//哦,原来敌人已经把我干掉了
			{
				//出现这种情况是因为在确认存活并换弹的过程中,有敌人把小明干掉了
				return false;
			}
			if ((step->second - 1) * bullet_time <= step->first - xm_index)//如果小明不能走到敌人的面前
			{
				//注意在判断时,不用考虑杀死最后一个敌人后的换弹时间
				xm_index += step->second * bullet_time;//确认存活,再换弹
				step++;
			}
			else//如果小明会die
			{
				return false;
			}
		}
	}
	//循环退出,小明存活
	return true;
}

int main()
{
	int max_time = 0;//存储最大时间
	int n;
	cin >> n >> shoot_distance;//读入敌人个数、射程
	int temp_key;//用于读入敌人的位置
	map<int, int>::iterator is_exist;
	for (int i = 0; i < n; i++)//读取敌人信息
	{
		cin >> temp_key;
		is_exist = enemy_map.find(temp_key);
		if (is_exist == enemy_map.end())//若当前位置没有敌人
		{
			enemy_map.insert(make_pair(temp_key, 1));
		}
		else//当前位置有敌人
		{
			is_exist->second += 1;//敌人数量+1
		}
	}
	int left = 0, right = int(1e7) + 1, middle;
	while (left + 1 != right)
	{
		middle = (left + right) / 2;
		if (is_die(middle))//小明成功存活,判断是否换弹时间能够更长
		{
			max_time = middle;
			left = middle;
		}
		else//小明被做掉了,换弹应该快一些
		{
			right = middle;
		}
	}
	cout << max_time << endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

WitheredSakura_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值