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<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
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;
}


發佈了57 篇原創文章 · 獲贊 8 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章