我来水文章了,它甚至毫无解释~
原问题:
题目来自代码源
解释(不是):
经典思路:从后向前推导,找出转移方程
集合:以 a[i] 结尾的所有单调上升子序列
目标:数量最大
转移方程:if(a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1) (i > j)
AC代码:
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
int a[1010], dp[1010], n;
int main(void)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
//初始化
for(int i = 1; i <= n; i++)
dp[i] = 1;
//找出以a[i]结尾的最长上升子序列dp[i]
for(int i = 1; i <= n; i++){
for(int j = 1; j < i; j++){
if(a[j] < a[i])
dp[i] = max(dp[i], dp[j] + 1);
}
}
//遍历输出最长上升子序列
int res = 0;
for(int i = 1; i <= n; i++)
res = max(res, dp[i]);
cout <<res <<endl;
return 0;
}
扩展一:求最大上升子序列和:
最大上升子序列和,那什么会不一样呢?
1.初始值,总不能还是 1 吧,肯定变 a[ i ] 了呀
2.转移方程
dp[ i ] = max( dp[ i ], dp[ j ] + a[ i ]);
代码:
#include <iostream>
using namespace std;
int a[10010], dp[10010], n;
int main(void)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
//找出以a[i]结尾的最大子序列和dp[i]
for(int i = 1; i <= n; i++){
dp[i] = a[i];
for(int j = 1; j < i; j++){
if(a[j] < a[i])
dp[i] = max(dp[i], dp[j] + a[i]);
}
}
//遍历查找最大子序列和
int res = 0;
for(int i = 1; i <= n; i++)
res = max(res, dp[i]);
cout <<res <<endl;
return 0;
}
扩展二:求最长的先上升后下降子序列
什么意思呢?就是字面意思
解决方法显而易见, 直接沿红线剪开,拆分成两个不同方向的最长上升子序列问题,搞定
代码:
#include <iostream>
using namespace std;
int a[10010], n;
int dp1[10010], dp2[10010];
int main(void)
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
//初始化
for(int i = 1; i <= n; i++)
dp1[i] = dp2[i] = 1;
//找出以a[i]结尾的最长上升子序列dp1[i]
for(int i = 1; i <= n; i++){
for(int j = 1; j < i; j++){
if(a[j] < a[i])
dp1[i] = max(dp1[i], dp1[j] + 1);
}
}
//找出以a[i]结尾的最长下降子序列dp2[i]
for(int i = n; i >= 1; i--){
for(int j = n; j > i; j--){
if(a[j] < a[i])
dp2[i] = max(dp2[i], dp2[j] + 1);
}
}
//遍历找出最大值
int res = 0;
for(int i = 1; i <= n; i++){
res = max(res, dp1[i] + dp2[i] - 1);
}
cout <<res <<endl;
return 0;
}