BSOJ1878 山头狙击战 解题报告

【问题描述】  
  
  为了掩护大部队,英雄Lucky被敌人包围在某山头。但他有足够的弹药,凭借自己的勇敢,完全可以将涌上来的敌人一个一个干掉。Lucky是个神枪手,只要他的枪膛中有子弹,他就能将在他射程m(用从敌人位置到山头的直线距离算)以内的一个敌人瞬间射杀。但如果在射程内没有敌人,出于节约子弹考虑,Lucky会等待敌人靠近然后射击。 


  正当Lucky为自己的强大而自我膨胀时,他忽然发现了一个致命的失误:他携带的枪是单发枪,每射出一发子弹都必须花K秒钟的时间装子弹。而凶残的敌人才不会花时间等你换子弹呢。他们始终在以1m/s的速度接近山头。而如果在一个敌人到达山头时Lucky无法将他击毙,那么我们可怜的Lucky就将牺牲在敌人的刺刀下。现在Lucky向你发出求助:要保住自己的性命并且歼灭所有敌人,Lucky最多只能用多少时间给枪装上一发子弹? 


  说明:假设一开始Lucky的枪中就有一发子弹,并且一旦确定一个装弹时间,Lucky始终会用这个时间完成子弹的装卸。希望你能帮助Lucky脱离险境。 
 
    
 【输入格式】  
  
  第一行有两个整数n和m,n代表敌人个数,m代表Lucky的射程。接下来有n行,每行一个整数di,代表每个敌人一开始相对山头的距离(单位为米)。 
 
    
 【输出格式】  
   
  仅有一个整数,代表Lucky的换弹时间(单位为秒)。
 
    
 【输入样例】   
   
6 100
236
120
120
120
120
120


 
    
 【输出样例】  
   
25


 
    
 【数据范围】  
   
50%的数据有:2<=n<=1000

100%的数据有:2<=n<=100,000; 1<=m<=10,000,000;1<=di<=10,000,000



解题思路:根据题意,要求最小换弹时间最大,主要算法肯定是求最小值最大问题的二分猜答案,对于每猜一个换弹时间,如果可行,则可以换弹时间再猜大点,如果不可行,换弹时间就要猜小点。这道题的难点在于如何判断猜的换弹时间是否可行,我们可以用一个变量tmp来记录当前Lucky所用的时间,先将敌人按相对山头的距离由小到大排序,然后从第二个敌人开始枚举(第一个敌人只要进入射程,就可以直接被射杀),先计算tmp,如果tmp大于该敌人到山头的时间,则答案不可行,否则就看下一个敌人。


#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=100005;
int N,M;
int d[maxn];
bool check(int m)  //判断猜的换弹时间是否可行
{
	int tmp=0;  //记录当前Lucky所用的时间
	if(d[1]>M)   tmp+=d[1]-M;  //第一个敌人还没有进入射程
	int i=2,ok=1;
	while(i<=N)  //枚举敌人
	{
		tmp+=m;  //将上一个敌人射杀,需换弹
		if(tmp>d[i])  
		{
			ok=0;
			break;
		}
		if(d[i]-tmp>M)   //如果换弹后敌人仍未进入射程,则可以在敌人进入射程之前换弹
		{
			tmp-=m;
			tmp+=d[i]-tmp-M;  //等待敌人进入射程
		}	
		i++;
	}
	if(ok==1)  return 1;
	return 0;
}
int main()
{
	freopen("48.in","r",stdin);
	//freopen("48.out","w",stdout);
	scanf("%d%d",&N,&M);
	for(int i=1;i<=N;i++)
	scanf("%d",&d[i]);
	int A=0,B=10000000,ans=0;
	sort(d+1,d+1+N);  //按敌人相对山头的距离由小到大排序
	for(int i=0;i<40;i++)  //二分猜答案
	{
		int m=(A+B)/2;
		if(check(m))  A=m+1,ans=m;
		else B=m-1; 
	}
	printf("%d\n",ans);
	return 0;
}


你可能感兴趣的:(分治算法)