洛谷p1020-导弹拦截(简单dp 最长上升子序列)

题目概况

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入样例:389 207 155 300 299 170 158 65
输出: 6 2

题目分析

读题可以看出这是求给定序列的最长不上升子序列的长度,不上升包括 相等+下降,以及能够划分为多少个最长不上升子序列的个数,第二问需要掌握一个组合数学的定理Dilworth定理,通俗理解就是:

DilWorth定理:一个数列的最长不升子序列的个数 = 最长上升子序列的长度 

知道了这个接下来就是求最长不上升子序列和最长上升子序列的长度了,下面只介绍求最长上升子序列的解法,题目要求如果要满分的话需要nlogn的时间复杂度,正常n*n的复杂度将只能获得一般分数!
O(n * n)算法:

1.维护一个一维数组 maxlen,maxlen[i]表示从第一个元素到第i个元素所能形成的最长上升子序列的长度;
2.构造状态转移方程。如果第j(j

O(nlogn)算法:

1.维护一个一维数组dp,即表示这个序列的最长上升子序列的所有值;
2.设定dp数组的下标标记top=1,将dp[top]与初始序列的每一个元素map[i]进行比较,
如果dp[to]>=map[i],则对dp数组进行二分查找,选择第一个大于map[i]的元素,并将该元素的数值更换为map[i];反之则将map[i]元素加到dp数组的后面,即dp[++top]=map[i];
因为第二重的循环为二分查找,所以时间复杂度为logn
3.序列所有元素都遍历完,输出top即为结果

AC代码

/*
DilWorth定理:一个数列最长不升子序列的个数 = 最长上升子序列的长度 
*/ 
#include
#include
#include
#include
#include 
#include
#include
#include
#include 
#include
using namespace std;

//int maxlen[100000+10];
int map[100000+10];
//int maxxlen[100000+10];
int dp[100000+10],dp2[100000+10];

int main()
{
	int n = 1;
	
	while(cin>>map[n])
	{
		n++;
	} n--;
	int top = 1;
	dp[1] = map[1]; //求最长不升子序列 
	for(int i=2;i<=n;i++)
		{
			if(dp[top]>=map[i]) 
				{
				dp[++top] = map[i];
				}
			else 
				{
					int l=1,r=top,m;
					while(l<=r) //二分查找
					{
						m=(l+r)/2;
						if(map[i]>dp[m]) r=m-1;
						else l=m+1;
					}
					dp[l] = map[i];	
				} 
		}
	cout<<top<<endl;
	dp2[1] = map[1];
	top=1;
	for(int i=2;i<=n;i++) //求最长上升子序列 
	{
		if(dp2[top]<map[i])
			dp2[++top] = map[i];
		else 
		{
				int l=1,r=top,m;
				while(l<=r) //在dp序列中寻找第一个比map[i]大的值的下标 
					{
						m=(l+r)/2;
						if(map[i]>dp2[m]) l=m+1;
						else r=m-1;
					}
				dp2[l] = map[i];	
		}

	}	
		cout<<top;	
} 




/*
// O(n*n)的 dp 算法  
int main()
{
	int n =1;
	while(cin>>map[n])
	{
		n++;
		maxlen[n-1] = 1;
		maxxlen[n-1] = 1;
	}
	n--; 
	for(int i=2;i<=n;i++) //求最长不升子序列! 
		for(int j=1;j=map[i]) 
				maxlen[i] = max(maxlen[i],maxlen[j]+1);
		}
	int Max=1;
	for(int i=1;i<=n;i++)
		if(Max

你可能感兴趣的:(动态规划dp)