题目意思:
给n个数,要求最长的递减子序列的长度,并求出不相同的个数(n<=5000)
例如,对于序列17,8,3,3,最长递减子序列为3,不同的个数为2(两个17,8,3是重复的!)
思路:
求最长递减子序列的长度很简单,有O(n*n)的做法,也有O(n*log(n))的做法
O(n*n)的做法是 d(i) = max{0,d(j) | j<i,A(j)<A(i) } + 1
d(i) 表示以第i位结尾的最长递减子序列的长度
O(n*log(n))的做法见刘汝佳的书
求不相同的个数也用dp:(注意要不相同)
考虑到要不相同,所以从最后j是最后一个满足 A[j] = A[i] 的位置(如果不存在则为-1)
num(i) = ∑num[k](j<k<i,A[k]>A[i],d[k]+1=d[i])
需要注意的是,这其中有把num(i)赋为1的小细节
最后再从头到尾扫一遍得到答案
最后,可以构造出
4999 5000 4997 4998 ..... 这样的样例,答案是2^2500,远远超出了整数范围,所以需要使用高精度
我觉得这样的样例会超出1s的时限。。。但是没有改成O(n*log(n))的做法....也不知道在统计个数的时候怎么处理
java中有现成的大整数的实现~
这里mark一下使用java中的大整数~
/*
ID: jasison2
TASK: buylow
LANG: JAVA
*/
/**
* @date 2014-04-17 20:25:56
* @author jasison
* @email jasison27@gmail.com
* @website http://www.jiangshan27.com
*/
import java.util.*;
import java.io.*;
import java.math.*;
public class buylow {
public static int n,ans1;
public static int[] d,a;
public static BigInteger ans2;
public static BigInteger[] num;
public static int N = 5003;
public static void main(String[] args) throws Exception {
d = new int[N];
a = new int[N];
num = new BigInteger[N];
BufferedReader infile = new BufferedReader(new FileReader("buylow.in"));
PrintWriter outfile = new PrintWriter(new BufferedWriter(new FileWriter("buylow.out")));
StringTokenizer st = new StringTokenizer(infile.readLine());
n = Integer.parseInt(st.nextToken());
int index = 0;
while (index < n) {
st = new StringTokenizer(infile.readLine());
while (st.hasMoreTokens()) {
a[index++] = Integer.parseInt(st.nextToken());
}
}
solve();
outfile.println(ans1+" "+ans2);
outfile.close();
}
public static void solve() {
ans1=0;
for(int i=0;i<n;++i){
d[i]=0;
num[i]=BigInteger.ZERO;
int st=i-1;
while(st>=0&&a[st]!=a[i]){
st--;
}
for(int j=st+1;j<i;++j){
if(a[i]<a[j]){
if(d[i]<d[j]){
d[i]=d[j];
num[i]=num[j];
}else if(d[i]==d[j]){
num[i]=num[i].add(num[j]);
}
}
}
if (num[i]==BigInteger.ZERO && st==-1){
num[i]=BigInteger.ONE;
}
d[i]++;
if(ans1<d[i]){
ans1=d[i];
ans2=num[i];
}else if(ans1==d[i]){
ans2=ans2.add(num[i]);
}
}
}
}