《算法笔记》11.1小节11.2小节11.3小节——动态规划专题->动态规划的递归写法和递推写法->最大连续子序列和->最长不下降子序列

11.1DP总览

11.1.1什么是动态规划

复杂问题分解为子问题,
分为递归和递推两种写法
递归写法又被称作记忆化搜索
一个问题必须拥有重叠子问题,才能使用动态规划来解决

11.1.2动态规划的递归写法

斐波那契数列
开一个一维数组dp,计算已经算过的结果
重叠子问题

11.1.3动态规划的递推写法

数塔问题
最优子结构:可以保证DP中原问题的最优解可以由子问题的最优解推导得到。
求fibonacci数列

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cmath>
#include<algorithm>

using namespace std;
const int maxn = 35;
int dp[maxn];
int n;
int F(int n)
{
	if (n == 0 || n == 1) return 1;
	if (dp[n] != -1) return dp[n];
	else
	{
		dp[n] = F(n - 1) + F(n - 2);
		return dp[n];
	}
}
int main() {
	while (scanf("%d", &n) != EOF)
	{
		if (n != 0)
		{
			for (int i = 0; i < maxn; i++)
				dp[i] = -1;
			printf("%d\n", F(n - 1));
		}
		else
			printf("0\n");
		
	}
	return 0;

}

11.2最大连续子序列和

状态的无后性
问题 A: 最大连续子序列
问题描述:给定K个整数的序列{ N1, N2, …, NK },其任意连续子序列可表示为{ Ni, Ni+1, …, Nj },其中 1 <= i <= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个,例如给定序列{ -2, 11, -4, 13, -5, -2 },其最大连续子序列为{ 11, -4, 13 },最大和为20。现在增加一个要求,即还需要输出该子序列的第一个和最后一个元素。

  • 输入
测试输入包含若干测试用例,每个测试用例占2行,第1行给出正整数K( K<= 10000 ),第2行给出K个整数,中间用空格分隔,每个数的绝对值不超过100。当K为0时,输入结束,该用例不被处理。
  • 输出
对每个测试用例,在1行里输出最大和、最大连续子序列的第一个和最后一个元素,中间用空格分隔。如果最大连续子序列不唯一,则输出序号i和j最小的那个(如输入样例的第23组)。若所有K个元素都是负数,则定义其最大和为0,输出整个序列的首尾元素。
  • 样例输入
5
-3 9 -2 5 -4
3
-2 -3 -1
0
  • 样例输出
12 9 5
0 -2 -1
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 10010;
struct DP {
	int sum;
	int pre;
} dp[maxn];
int A[maxn];
int n;
int flag;
int main() {
	while (scanf("%d", &n) != EOF)
	{
		flag = 0;
		if (n == 0)
			break;
		else
		{
			for (int i = 0; i < n; i++)
			{
				scanf("%d",&A[i]);
				if (A[i] >= 0)
					flag = 1;
					
			}
			if (flag == 1)
			{
				dp[0].sum = A[0];
				dp[0].pre = 0;
				for (int i = 1; i < n; i++)
				{
					if (dp[i-1].sum>0)
					{
						dp[i].sum = dp[i - 1].sum + A[i];
						dp[i].pre = dp[i - 1].pre;
					}
					else
					{
						dp[i].sum = A[i];
						dp[i].pre = i;
					}					
				}
			/*	for (int i = 0; i < n; i++)
				{
					printf("%d ",dp[i].sum);
				}
				printf("\n");
				for (int i = 0; i < n; i++)
				{
					printf("%d ", A[i]);
				}
				printf("\n");*/
				int k = 0;
				for (int i = 1; i < n; i++)
				{
					if (dp[i].sum > dp[k].sum)//因为要输出序号最小的那个,所以是>号
						k = i;
				}
				printf("%d %d %d\n", dp[k].sum,A[dp[k].pre],A[k]);
			}
			else
			{
				printf("0 %d %d\n",A[0],A[n-1]);
			}			
		}
		
	}
	return 0;

}

11.3最长不下降子序列(LIS)

问题 A: 最长上升子序列
问题描述:一个数列ai如果满足条件a1 < a2 < … < aN,那么它是一个有序的上升数列。我们取数列(a1, a2, …, aN)的任一子序列(ai1, ai2, …, 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个自然数ai,0 <= ai <= 10000,两个数之间用空格隔开。
  • 输出
在这里插入代码片输出只有一行,包含一个整数,表示最长上升子序列的长度。```
- 样例输入
 ```cpp
7
1 7 3 5 9 4 8
  • 样例输出
4
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 1010;
int dp[maxn];
int A[maxn];
int n;
int main() {
	while (scanf("%d", &n) != EOF)
	{
		for (int i = 1; i <= n; i++)
		{
			scanf("%d",&A[i]);
		}
		int ans = -1;
		for (int i = 1; i <= n; i++)
		{
			dp[i] = 1;
			for (int j = 1; j < i; j++)
			{
				if (A[j] <= A[i] && (dp[j] + 1 > dp[i]))
					dp[i] = dp[j] + 1;
			}
			ans = max(ans, dp[i]);
		}
		printf("%d\n",ans);
		
	}
	return 0;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值