题意简述:就是说给出一个序列,求出其最长下降子序列(类比最长上升子序列),以及个数。同时要求统计个数时不能有重复,重复的意思是该序列每一个对应元素与另一个序列相等。
原题链接:POJ1952
解题思路:又一次栽在题意上,我忽略了子串重复这个要求,导致我刚开始的做法与正确答案大相径庭。具体思路是,在求最短下降子序列的过程中想办法求出其个数。而如何求出其个数成了这题最后一个难点。
下面的代码主体部分是16到28行,其本质上是求最长降序子序列。但是可以看见,引入的num和vis两个数组,前者存放到 i 为止,不同的长度最长的序列的个数,后者则是一个标记数组。首先 j 要从i-1向前数,这是因为如果a [ i ] == a[ j ],那么 j 前的状态和 i 就相同了,就可以跳出循环,所以要从后向前遍历。然后就是标记数据vis的使用,vis[ i ] = 1表示 i 前没有比a[ i ] 更大的数,那此时(因为a[ j ] == a[ i ])a[ i ] 就可以被a[ j ] 替代。
代码示例:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn = 5000+50;
int a[maxn];
int b[maxn];
int num[maxn];
int vis[maxn];
void solve(int n){
memset(num,0,sizeof num);
memset(vis,0,sizeof vis);
for(int i = 0;i < n;i++){
b[i] = 1;
num[i] = 1;
for(int j = i-1;j >= 0;j--){
if(a[i] < a[j] && !vis[j]){
if(b[j]+1 == b[i]){
num[i] += num[j];
}else if(b[i] < b[j]+1){
b[i] = b[j]+1;
num[i] = num[j];
}
}else if(a[i] == a[j]){
if(b[i] == 1) vis[i] = 1;
break;
}
}
//printf("%d ",b[i]);
}
int len = 0,cnt = 0;
for(int i = 0;i < n;i++) len = max(len,b[i]);
for(int i = 0;i < n;i++){
if(b[i] == len){
cnt += num[i];
}
}
printf("%d %d\n",len,cnt);
}
int main(){
int n;
while(scanf("%d",&n) != EOF){
for(int i = 0;i < n;i++) scanf("%d",a+i);
solve(n);
}
}