题目描述
在一个数字序列中,找到一个最长的非连续子序列,使得这个子序列是不下降(非递减)。现有序列A={1,2,3,-1,-2,7,9},则A的最长不下降子序列是{1,2,3,7,9}。
如果有多个最长序列,只需选数字顺位靠后的序列从大到小输出。
输入要求
输入2行;
第一行一个整数n,表示有n个整数的序列要输入,n<1000;
第二行共有n个整数。
输出要求
输出最长的不下降子序列,只需选数字顺位靠后的序列从大到小输出。
输入样例
7
31 96 27 -35 46 -96 0
输出样例
2
0 -96
提示
如何找到第i个数字到末尾的最长非减序列,关键就是找到第i个数字后的数字a[j]
a[j]符合非递减规则,并且其到末尾距离的非减序列是最长的
这样第i个数字到末尾的最长非减序列dp[j]+1
数字i从n-2到0,每次问题的求解都需要前面已经求的解,显然可以使用动态规划
来源
NBU OJ
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1000+10;
int a[maxn]; // 输入的序列
int dp[maxn]; // dp[i]表示第i+1个数到最后一个数的最长非减序列长度
int index[maxn]; // 记录最长非减序列的索引
// index[i]表示序列中索引i的数字的下一个数字的索引
int n;
int main()
{
// 输入数据
cin >> n;
for(int i=0; i<n; i++)
cin >> a[i];
// 初始化
for(int i=0; i<n; i++){
dp[i] = 1;
index[i] = 0;
}
//找到每个数字到终点的最长非减序长度
for(int i=n-2; i>=0; i--){
int length_i = 0;
for(int j=i+1; j<n; j++){
if(a[j]>=a[i] && dp[j]>=length_i){ // 找到数字a[j],满足非递减且到末尾长度最大,如果有多个,选择靠后的
length_i = dp[j];
index[i] = j; // 最长序列中i的下一个数字的位置
}
}
if(length_i)
dp[i] = length_i + 1;
}
int max_length = 0, p;
for (int i = 0; i < n; ++i)
if (dp[i] >= max_length){ // 找到数字a[i],其到末尾的非减序列最长,如有多个,选择靠后的
max_length = dp[i];
p = i;
}
int result[max_length]; // 保存最长非减序列
for(int i=0; i<max_length; i++){
result[i] = a[p];
p = index[p];
}
sort(result, result+max_length); // 将得到序列从小到大排序
cout << max_length << endl;
for(int i=max_length-1; i>=0; i--) // 从大到小输出
{
if(i == 0) // 最后一个输出不要空格
cout << result[i];
else
cout << result[i] << " ";
}
cout << endl;
return 0;
}
问题简化版:
#include<iostream>
#include<algorithm>
using namespace std;
int max(int a,int b)
{
if(a>b) return a ;
else return b;
}
int main()
{
int n;
cin>>n;
int i,a[1000];
int dp[100];
for(i=0;i<n;i++)
{
cin>>a[i];
dp[i]=1;
}
for(i=n-1;i>=0;i--)
{
for(int j=i+1;j<=n-1;j++)
{
if(a[j]>=a[i])
{
dp[i] = max(dp[i],dp[j]+1);
}
}
}
int length_index=0,max_length=dp[0];
for(i=0;i<n;i++)
{
if(dp[i]>=max_length)
{
length_index = i;
max_length = dp[i];
}
}
cout<<"最大子序数:"<<max_length<<" "<<"开始节点(0开始):"<<length_index<<endl;
return 0;
}