zoukankan      html  css  js  c++  java
  • 最长上升子序列题目大合集

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


    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
    ",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
    ",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);
    }
    感激不敬!!

  • 相关阅读:
    VUE组件间传参
    JS-03 (RegExp对象&字符串总结)
    JS-02 (字符串的正则函数)
    理解 C++ 的 Memory Order
    GCC的原子操作函数
    barrier内存屏障
    tcp重传机制,流量控制,拥塞控制
    TCP 的拥塞控制
    Monitoring and Tuning the Linux Networking Stack: Receiving Data
    kernel网络之软中断
  • 原文地址:https://www.cnblogs.com/Darknesses/p/12002562.html
Copyright © 2011-2022 走看看