最长不下降子序列
展开
题目描述
设有由 �n 个不相同的整数组成的数列,记为:�1,�2,...,��a1,a2,...,an,例如 3,18,7,14,10,12,23,41,16,243,18,7,14,10,12,23,41,16,24。若存在 �1<�2<�3<⋯<��i1<i2<i3<⋯<ie 且有 ��1<��2<⋯<���ai1<ai2<⋯<aie 则称为长度为 �e 的不下降序列。如上例中 3,18,23,243,18,23,24 就是一个长度为 44 的不下降序列,同时也有 3,7,10,12,16,243,7,10,12,16,24 长度为 66 的不下降序列。程序要求,当原数列给出之后,求出最长的不下降序列。
输入格式
第一行为 �n,表示 �n 个数
第二行 �n 个整数,数值之间用一个空格分隔
输出格式
最长不下降子序列的长度 。
样例
输入数据#1
3
1 2 3
Copy
输出数据#1
3
Copy
解释#1
123123 构成了最长的不下降子序列,总个数为 33
输入数据#2
7
1 7 3 5 9 4 8
Copy
输出数据#2
4
Copy
解释#2
13591359 构成了最长的不下降子序列,总个数为 44
数据范围
�<1000n<1000
考点:
动态规则
代码:
方法1:深搜,但是会超时
/*****author:ssxyz*****/
#include <bits/stdc++.h>
using namespace std;
/*
* 分析:每个数最多有“选”和“不选”两种选择,因此可以用搜索来完成。
*
* 关键代码:定义搜索dfs(int pre, int curr, int currlen),
* 其中pre表示当前不下降子序列的最后一个元素a[pre],现在搜索考虑第curr个元素,
* 当前不下降子序列的长度为currlen,通过确定a[curr]选还是不选,这样可以把所有不下降子序列都搜索出来。
*
* 本程序在数据稍大时,就会超时,因为进行了重复搜索。
* 比如样例:1 7 3 5 9 4 8
* 搜索过程中会调用dfs(3, 4, 1)和dfs(3, 4, 2),分别对应着3 和 1 3 这两个中间状态
* 在实际执行过程中dfs(3, 4, 1)和dfs(3, 4, 2)后续的搜索时完全一样的,这里存在重复搜索。
* 在具体一点,不下降子序列的最后一个元素相同时执行的后续搜索是一样的。
* 可以在搜索过程中可以加上最优化剪枝。
*/
const int MAXN = 5005;
int a[MAXN], N, ans;
void dfs(int pre, int curr, int currlen)
{
if (curr == N + 1)
{
if (currlen > ans)
ans = currlen;
return;
}
if (a[curr] >= a[pre])
dfs(curr, curr + 1, currlen + 1);//选a[curr]
if (currlen + N - curr > ans)
dfs(pre, curr + 1, currlen);//不选a[currlen],最优化剪枝
}
int main()
{
cin >> N;
for (int i = 1; i <= N; i++)
{
cin >> a[i];
}
ans = 0;
a[0] = -(1 << 30);//赋值为最小值
dfs(0, 1, 0);
cout << ans << endl;
return 0;
}
方法2:动态规划
��dp 数组∶存储状态,以 �[�]a[i] 结尾的最长不下降子序列的长度
-
dp[1]=1
,以第11个数结尾的最长不下降子序列就是�[1]a[1]自己,因此有11个 -
dp[2]=2
,因为 �[2]>�[1]a[2]>a[1],因此�[2]a[2]可以续到 �[1]a[1]的末尾 -
dp[3]
:��[1]+1=2dp[1]+1=2,因为 �[3]a[3] 能够续到 �[1]a[1] 的结尾 -
dp[4]
:将 1 ~ 3 都讨论一遍,看可以续在哪个数的后面,且找到不下降子序列长度的最大值!-
可以续到 �[1]a[1] 的后面:��[1]+1=2dp[1]+1=2
-
可以续到 �[3]a[3] 的后面:��[3]+1=3dp[3]+1=3
-
…………
归纳动态转移方程∶
��[�]=1dp[i]=1
��[�]=���(��[�]+1,��[�])dp[i]=max(dp[j]+1,dp[i]),其中 �j 是 11 到 �−1i−1 之间的数,�[�]<�[�]a[j]<a[i]
/*****author:ssxyz*****/
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int a[N], dp[N];
int main()
{
int n, maxn = 0;
cin>>n;
for(int i = 1; i <= n; i++)
{
cin>>a[i];
dp[i] = 1;
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j < i; j++)//找一遍所有的可能性
{
if(a[i] >= a[j])//如果是非严格递增的
{
dp[i] = max(dp[j] + 1, dp[i]);//更新最大值
}
}
if(dp[i] > maxn)
{
maxn = dp[i];
}
}
cout << maxn;
}
方法3:(骗分,水代码)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1003;
int a[maxn], f[maxn];
int n,ans = INT_MIN;
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>a[i];
f[i] = 1;
}
for(int i=1; i<=n; i++)
for(int j=1; j<i; j++)
if(a[j] <= a[i])
f[i] = max(f[i], f[j]+1);
for(int i=1; i<=n; i++)
ans = max(ans, f[i]);
cout<<ans;
return 0;
}