动态规划解决---最长递减(或递增)序列

定义:在一个没有排好序的数组中,找最长的单调递减或单调递增的序列。

解题思路:通过动态规划解题。假设从0--(i - 1)已经构成了长度为s的递减序列,且这些序列中的末尾值中的最大值为t;

1、如果a[i] < t,则假如到这个序列中去,长度变为s + 1。对应的末尾值变为a[i]。

2、如果a[i] == t, 则说明从0 - i 的最长序列长度为s。

3、如果a[i] > t,a[i]不一定能成为最长序列的末尾值。此时取绝于长度为s - 1的序列的末尾最大值是max。如果max > a[i],则他可以成为长度为s的递减序列。如果max < a[i], 继续往前推, 直到a[i]小于某个末尾的最大值,此时长度为k。那么a[i]是0-i中长度为k + 1的递减序列的最大值。

两个实例:

1.拦截导弹

题目描述:

某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。

参考代码:

#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm> // 直接使用max函数 
using namespace std;
int a[30]; 
int main(){
    int k;
    while(cin >> k){
        for(int i = 0; i < k; i ++){// 输入序列 
            cin >> a[i];
        }
        vector<int> dp(k, 1);// 初始化 
        dp[0] = 1;
        int maxVal = 1;
        for(int i = 1; i < k; i ++){
            for(int j = i; j > 0; j --){// 看前面的数是否比我(a[i])大 
            	if(a[i - j] >= a[i]){// 不同于递减的情况,是不高于 
            		dp[i] = max(dp[i], dp[i - j] + 1);
				}
			} 
			if(maxVal < dp[i]){
				maxVal = dp[i];
			}
        }
        
//        for(int i = 0; i < k; i ++){// 将每个序列长度输出 
//        	cout << dp[i] << " ";
//		}
//		cout << endl;
        cout << maxVal << endl;
    }
    return 0;
}

2.最大上升序列和

一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ...,aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中序列和最大为18,为子序列(1, 3, 5, 9)的和. 你的任务,就是对于给定的序列,求出最大上升子序列和。注意,最长的上升子序列的和不一定是最大的,比如序列(100, 1, 2, 3)的最大上升子序列和为100,而最长上升子序列为(1, 2, 3)。

参考代码:

这题和上题的解题思路很类似,有些许的改动处,我在代码中已经给出了注释。

#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm> // 直接使用max函数 
using namespace std;
int a[1001], dp[1001]; 
int main(){
    int n;
    while(cin >> n){
        for(int i = 0; i < n; i ++){// 输入序列 
            cin >> a[i];
        }
        memset(dp, 0, sizeof(dp));// 初始化 
        int maxVal = a[0];
        for(int i = 0; i < n; i ++){
        	dp[i] = a[i];// dp[i]的初值就是a[i],找到比它小的相加即可 
            for(int j = 0; j < i; j ++){// 找前面的数是否都比我小
            	if(a[j] < a[i]){
            		// dp[j]就是之前的各值得和,加上这次得标志位a[i] 
            		dp[i] = max(dp[i], dp[j] + a[i]);
				}
			} 
			if(maxVal < dp[i]){// 每次比较一下,最后保留一个最大值,也可以做完之后,遍历dp数组 
				maxVal = dp[i];
			}
        }
        
//        for(int i = 0; i < n; i ++){// 输出每组和的结果 
//        	cout << dp[i] << " ";
//		}
//		cout << endl;
        cout << maxVal << endl;
    }
    return 0;
}



以上是这篇的主要内容。欢迎您提出宝贵的意见,让我们一同进步。谢谢!

猜你喜欢

转载自blog.csdn.net/kobe_jr/article/details/80643627