JDOJ 1946 求最长不下降子序列个数
Description
设有一个整数的序列:b1,b2,…,bn,对于下标i1<i2<…<im,若有bi1≤bi2≤…≤bim
则称存在一个长度为m的不下降序列。
现在有n个数,请你求出这n个数的最长不下降序列的长度及有多少个最长不下降序列
Input
第一行为一个整数n (n < 104)
第二行有n个整数,数与数之间使用空格间隔
Output
第一行,最长不下降序列的长度
第二行,能构成多少个最长不下降序列(数字相同,位置不同算不同)
Sample Input
7
1 4 3 2 6 5 7
Sample Output
4
6
这道题综合了求最长不降子序列长度和个数,是一道不折不扣的好题。
需要一定的动归基础,0基础同学请自动回避本博客。
定义两个数组,dp数组表示以i结尾的最长不降子序列长度,
len数组表示以i结尾的最长不降子序列的个数。
我们处理第一问的时候,在输出上加一些处理。
求最长不降子序列的时候,先初始化dp[i]=1;不需要解释。
然后内层循环从1到i-1开始扫。如果符合条件(a[j]<=a[i])
就转移,dp[i]取dp[i]和dp[j]+1的较大值。
这些都不需要解释,。
然后定义标记变量vis,初值一定要设置1。
从头扫,不断更新最长的长度。
最后求出最最最长的长度dp[vis]输出。
然后是本题的重头戏。
第二次动归。
首先是初值,初值要设置成1,前提条件是dp[i]得等于1.因为只有长度为1的最长不降子序列的个数是唯一确定的,
然后可以开始扫描了。
这里要注意这个条件,着重着重着重!!!理解!!
dp[j]+1==dp[i]
为什么呢??
注意题面,数字相同位置不同算不同,就是这个意思。
好好理解
然后就可以输出答案了,原理和上面的大同小异。
代码:
#include<cstdio> #include<algorithm> using namespace std; int n,ans,vis; int a[10001]; int dp[10001];//表示以i结尾的最长不下降序列的长度。 int len[10001];//表示以i为结尾的最长不降序列的个数. int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { dp[i]=1; for(int j=1;j<i;j++) if(a[j]<=a[i]) dp[i]=max(dp[j]+1,dp[i]); } vis=1; for(int i=1;i<=n;i++) if(dp[i]>dp[vis]) vis=i; printf("%d\n",dp[vis]); for(int i=1;i<=n;i++) { if(dp[i]==1) len[i]=1; else for(int j=1;j<i;j++) if(a[j]<=a[i] && dp[j]+1==dp[i]) len[i]+=len[j]; } for(int i=1;i<=n;i++) if(dp[i]==dp[vis]) ans+=len[i]; printf("%d",ans); return 0; }