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最小的那个(如输入样例的第2、3组)。若所有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;
}