描述
设有整数序列b1,b2,b3,…,bm,若存在i1<i2<i3<…<in,且bi1<bi2<bi3<…<bin,则称 b1,b2,b3,…,bm中有长度为n的不下降序列bi1,bi2,bi3,…,bin。求序列b1,b2,b3,…,bm中所有长度(n)最大不下降子序列
具有相同元素的序列,我们称之为重复序列,这里我们不统计重复序列,也即是说,重复的是算一次
具有相同元素的序列,我们称之为重复序列,这里我们不统计重复序列,也即是说,重复的是算一次
输入格式
第一行为m,表示m个数(m<=900)
第二行m个数
第二行m个数
输出格式
第一行输出最大长度n
第二行输出长度为n的序列个数Total
第二行输出长度为n的序列个数Total
测试样例1
输入
3
1 2 2
输出
2
1
题目描述的是最长上升子序列
至今仍然觉得这道题的思路很奇怪。在序列的后面加一个极大的数,其实这是用来存答案的。然后做最长上升,做完之后寻找答案的过程其实还是一个dp,由前面的点去扩展后面的点。但是要注意的是,如果当前点和后面有一个点重复,那么直接退出防止多算(因为对于后面的数来说,前面有两个一样的是没有影响的)
其实也就是说用离他最近的那一个符合条件的点去扩展他。
要注意不能把相同的数的前面那个删去。反例比如说13235。但是把相邻的相同的数删掉是完全可以的。
这道题隔一段时间应该看一看。但是还是觉得时间复杂度十分的不科学
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#define inf 2100000000
using namespace std;
int n1,n,Max;
int f[1005],a[1005],h[1005],ans[1005];
bool b[1005];
int main(){
scanf("%d",&n1);
for (int i=1;i<=n1;++i) scanf("%d",&a[i]);
for (int i=2;i<=n1;++i)
if (a[i]==a[i-1]) b[i]=true;
n=0;
for (int i=1;i<=n1;++i)
if (!b[i]) h[++n]=a[i];
h[++n]=inf;
f[1]=1;
for (int i=2;i<=n;++i){
for (int j=1;j<i;++j)
if (h[i]>h[j]&&f[i]<f[j])
f[i]=f[j];
f[i]++;
}
for (int i=1;i<=n;++i)
Max=max(f[i],Max);
for (int i=1;i<=n;++i) if (f[i]==1) ans[i]=1;
for (int i=1;i<=Max;++i)
for (int j=i;j<=n;++j)
if (f[j]==i)
for (int k=j+1;k<=n;++k){
if (f[k]==f[j]+1&&h[k]>h[j])
ans[k]+=ans[j];
if (h[k]==h[j]) break;
}
Max--;
printf("%d\n%d\n",Max,ans[n]);
}