湖南大学ACM10054——导弹拦截问题

问题描述

湖南大学ACM10054——导弹拦截问题_第1张图片
按照给定导弹的高度计算出一枚导弹最多可以拦截多少个敌方导弹,这些导弹最少需要多少枚去拦截。

题目分析

题目的思想一目了然,属于动态规划和贪心法结合。对于第一问,一枚导弹最多可以拦截几枚敌方导弹,这个问题和我们之前说过的最长单调子序列是一样的,详细可见最长单调递增子序列这不过这个问题我们要求单调递减子序列,但是解法一样。
第二问,最少的导弹拦截系统,即我们需要几枚导弹可以全部拦截掉。我们显然可以想到贪心算法。每次找到可以拦截最大高度的导弹,这样就可以让一次拦截较多敌方导弹,最后结果就是最小的。
但是!!!!实测这个方法会超时,这道题我用了很久,我可以确保我的代码没问题,O(nlogn)的最长单调递减子序列求解和O(n^2)的贪心求解。最后的时间复杂度应该也是O(n的平方)。但是结果确实超时了,我们只能选择另一种解法。
在这里我发现了一个问题,这两个问题其实是相同的,如果一个导弹能拦截的敌军越多,自然第二问需要的导弹系统越少。所以我们可以这样思考,那为什么还需要多个导弹系统呢?是因为有递增的序列,会导致我们不得不增加系统来拦截。这又变成了一个和第一问相对的问题,我们可以提出假设:最少导弹系统数=最长单调递增子序列。
这里大家可以根据这里的思路一步一步走,其实还是挺合理的,因为我们不能接受单调递增地拦截,所以为了处理单调递增的我们只能为每一个分配一个拦截系统。虽然我并不能给出公式的证明,但是这个是显然成立的,经过验证我们也可以发现其成立。

代码

#include 
using namespace std;
#define maxx 30000
#define max(a,b) ((a)>(b)?(a):(b))//找最大值
int main(){    
	int a[maxx],up[maxx]={0},down[maxx]={0};    
	int i=0,j,k,ans=0,cnt=0;    
	char cc;   
	while((cc=getchar())==' ')
	{        
		cin>>a[i++];    
	}    
	for(k=0;k<i;k++)
	{//共i个导弹        
		up[k]=1; //up[k]为以a[k]为结尾的最长不下降子序列的长度        、
		down[k]=1; //down[k]为以a[k]为结尾的最长下降子序列的长度        
		for(j=0;j<k;j++){            
			if(a[j]>a[k])
			{//后一个比当前这个矮            
				//最长下降子序列                		
				down[k]=max(down[j]+1,down[k]);//找其中的最大值            
				}            
				else
				{            
				//最长不下降子序列                
					up[k]=max(up[j]+1,up[k]);            
				}        
			}        
			ans=max(ans,down[k]);        
			cnt=max(cnt,up[k]);    
	}    
	cout<<ans<<" "<<cnt;    
	return 0;
}

总结

这个题目还是要深入研究一下,争取给出一个证明方案。唯一想不通的地方还是为什么利用二分法找最长单调递增子序列和贪心法找最小拦截系统会超时。以后再拿出来讨论吧

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