最长上升子序列题目大合集

先看看第一题,再由点到面地延伸:


1759:最长上升子序列

描述 一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1a2, ..., aN),我们可以得到一些上升的子序列(ai1ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).

你的任务,就是对于给定的序列,求出最长上升子序列的长度。
输入 输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。 输出 最长上升子序列的长度。 样例输入
7
1 7 3 5 9 4 8
样例输出
4


这道题就是这个合集最经典的题了,也是动态规划的基础题(声明:本人动归掌握不好,如有错误,请原谅)

首先,动归和贪心不一样,不能由局部最优达到全局最优,所以解题的思路就是最大与最小,这里我们可以用一个新数组来辅助记录

a[ i ]表示第i个数,而h[ i ]表示到第i个数时,它是第几个从第“零”个数延伸到的,相当于如下表格:


程序实现相当于,每访问一个数i,就往前找,直到找到一个比其小的数j,那么h[ i ] = h[ j ]+1

最后找出整个h里最大的就行了

插一句,找一个一维数组里最大的可以使用:s = *max_element(a+1,a+n+1)来实现,要使用头文件algorithm,最小则把max改成min

代码实现:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[5001][2];
int main()
{
	int i=1,j,n;
	int ss=0;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i][0]);
		a[i][1]=1;
		for(j=i-1;j>0;j--)
			if(a[j][0]<a[i][0])
				a[i][1]=max(a[j][1]+1,a[i][1]);
	}
	for(i=1;i<=n;i++)
		ss=max(ss,a[i][1]);
	printf("%d\n",ss);
}

代码不必多说了吧~


8780:拦截导弹

描述

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

输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹。

输入 第一行是一个整数N(不超过15),表示导弹数。
第二行包含N个整数,为导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)。 输出 一个整数,表示最多能拦截的导弹数。 样例输入
8
389 207 155 300 299 170 158 65
样例输出
6

这道题与上道题几乎一模一样,相当于求一组数里的最长不上升子序列,解法相同

代码就不必丢了吧~


4977:怪盗基德的滑翔翼

描述

怪盗基德是一个充满传奇色彩的怪盗,专门以珠宝为目标的超级盗窃犯。而他最为突出的地方,就是他每次都能逃脱中村警部的重重围堵,而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。

有一天,怪盗基德像往常一样偷走了一颗珍贵的钻石,不料却被柯南小朋友识破了伪装,而他的滑翔翼的动力装置也被柯南踢出的足球破坏了。不得已,怪盗基德只能操作受损的滑翔翼逃脱。

假设城市中一共有N幢建筑排成一条线,每幢建筑的高度各不相同。初始时,怪盗基德可以在任何一幢建筑的顶端。他可以选择一个方向逃跑,但是不能中途改变方向(因为中森警部会在后面追击)。因为滑翔翼动力装置受损,他只能往下滑行(即:只能从较高的建筑滑翔到较低的建筑)。他希望尽可能多地经过不同建筑的顶部,这样可以减缓下降时的冲击力,减少受伤的可能性。请问,他最多可以经过多少幢不同建筑的顶部(包含初始时的建筑)?


输入 输入数据第一行是一个整数K(K < 100),代表有K组测试数据。
每组测试数据包含两行:第一行是一个整数N(N < 100),代表有N幢建筑。第二行包含N个不同的整数,每一个对应一幢建筑的高度h(0 < h < 10000),按照建筑的排列顺序给出。 输出 对于每一组测试数据,输出一行,包含一个整数,代表怪盗基德最多可以经过的建筑数量。 样例输入
3
8
300 207 155 299 298 170 158 65
8
65 158 170 298 299 155 207 300
10
2 1 3 4 5 6 7 8 9 10
样例输出
6
6
9

由于是从两个方向逃跑,即从两端开始,向中间求两次最长上升子序列,找最大就行了,多组数据,注意清空

代码如下:
#include<cstdio>
#include<algorithm>
using namespace std;
int a[1001],h1[1001],h2[1001],l,r,n;
int main()
{
	int i,j,nn;
	scanf("%d",&nn);
	while(nn)
	{
		nn--;
		scanf("%d",&n);
		l=0;
		r=0;
		for(i=0;i<n;i++)
			scanf("%d",&a[i]);
		for(i=0;i<n;i++)
		{
			h1[i]=1;
			for(j=0;j<i;j++)
			{
				if(a[j]<=a[i])
					h1[i]=max(h1[j]+1,h1[i]);
			}
			l=max(l,h1[i]);
		}
		for(i=n-1;i>=0;i--)
		{
			h2[i]=1;
			for(j=n-1;j>i;j--)
			{
				if(a[j]<=a[i])
					h2[i]=max(h2[j]+1,h2[i]);
			}
			r=max(r,h2[i]);
		}
		printf("%d\n",max(l,r));
	}
}

1996:登山

描述

五一到了,PKU-ACM队组织大家去登山观光,队员们发现山上一个有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号。同时队员们还有另一个登山习惯,就是不连续浏览海拔相同的两个景点,并且一旦开始下山,就不再向上走了。队员们希望在满足上面条件的同时,尽可能多的浏览景点,你能帮他们找出最多可能浏览的景点数么?

输入 Line 1: N (2 <= N <= 1000) 景点数
Line 2: N个整数,每个景点的海拔 输出 最多能浏览的景点数 样例输入
8
186 186 150 200 160 130 197 220
样例输出
4

这题就有点复杂了,首先,将从左到右的最长上升子序列求出,再由每个点求到终点的下降子序列,找和最大的输出
代码如下:
#include<cstdio>
#include<algorithm>
using namespace std;
int a[1001],h1[1001],h2[1001],k,g=-1;
void work(int x)
{
	int i,j;
	for(i=g;i>=x;i--)
	{
		h2[i]=1;
		for(j=g;j>i;j--)
		{
			if(a[j]<a[i])
				h2[i]=max(h2[j]+1,h2[i]);
		}
	}
	k=max(k,h2[x]+h1[x]-1);
}
int main()
{
	int n,h,i,j;
	scanf("%d",&n);
	for(i=0;i<n;i++)
	{
		scanf("%d",&h);
		if(h!=a[g])
		{
			g++;
			a[g]=h;
		}
	}
	for(i=0;i<=g;i++)
	{
		h1[i]=1;
		for(j=0;j<i;j++)
		{
			if(a[j]<a[i])
				h1[i]=max(h1[j]+1,h1[i]);
		}
		work(i);
	}
	printf("%d",k);
}

90:滑雪

描述 Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道载一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。下面是一个例子
 1  2  3  4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可滑行的滑坡为24-17-16-1。当然25-24-23-...-3-2-1更长。事实上,这是最长的一条。 输入 输入的第一行表示区域的行数R和列数C(1 <= R,C <= 100)。下面是R行,每行有C个整数,代表高度h,0<=h<=10000。 输出 输出最长区域的长度。 样例输入
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
样例输出
25

这题我用的三重枚举来更新每个点的最大距离,如果有更好的方法,在评论区留言教教我,O(∩_∩)O谢谢

代码如下:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
using namespace std;
int a[120][120],h[120][120],m,n,longest;
void ill()
{
	int i,j;
	for(i=0;i<m;i++)
		for(j=0;j<n;j++)
		{
			if(a[i-1][j]>a[i][j]&&i>0)
				h[i][j]=max(h[i-1][j]+1,h[i][j]);
			if(a[i+1][j]>a[i][j]&&i<m-1)
				h[i][j]=max(h[i+1][j]+1,h[i][j]);
			if(a[i][j-1]>a[i][j]&&j>0)
				h[i][j]=max(h[i][j-1]+1,h[i][j]);
			if(a[i][j+1]>a[i][j]&&j<n-1)
				h[i][j]=max(h[i][j+1]+1,h[i][j]);
			longest=max(longest,h[i][j]);
		}
	for(i=m-1;i>=0;i--)
		for(j=n-1;j>=0;j--)
		{
			if(a[i-1][j]>a[i][j]&&i>0)
				h[i][j]=max(h[i-1][j]+1,h[i][j]);
			if(a[i+1][j]>a[i][j]&&i<m-1)
				h[i][j]=max(h[i+1][j]+1,h[i][j]);
			if(a[i][j-1]>a[i][j]&&j>0)
				h[i][j]=max(h[i][j-1]+1,h[i][j]);
			if(a[i][j+1]>a[i][j]&&j<n-1)
				h[i][j]=max(h[i][j+1]+1,h[i][j]);
			longest=max(longest,h[i][j]);
		}
}
int main()
{
	int i,j;
	scanf("%d%d",&m,&n);
	for(i=0;i<m;i++)
		for(j=0;j<n;j++)
			h[i][j]=1;
	for(i=0;i<m;i++)
		for(j=0;j<n;j++)
			scanf("%d",&a[i][j]);
	for(i=0;i<100;i++)
		ill();
	printf("%d",longest);
}
感激不敬!!

posted @ 2016-11-11 14:23  Izayoi_Doyo  阅读(545)  评论(0编辑  收藏  举报