典型的动态规划,难点是第二步,cnt[i]表示以a[i]结尾的最长下降序列的种数,则
cnt[i] = count( len[k]+1==len[i]), 1<=k < i , 其中len[i]表示以a[i]结尾的最长下降子序列长度
还有一个问题,如果生成的实际序列值相等,则算作一种,可以通过一下方法处理:
计算出a[i]之后和第一个和a[i]相等的数a[k],令next[i] = k; 如果没有相等的则next[i] = 0
计数时如果 next[j]!=0 && next[j] < i ,则不累计,因为位于j和i之间的数里存在和a[i]相等的,从而可以以a[next[j]]替换a[j]来得
到相同的序列; 同时,累加的过程是指数级增长的,所以要用高精度
- /*
- PROG: buylow
- LANG: C++
- ID: heben991
- */
- #include <iostream>
- #include <algorithm>
- using namespace std;
- const int N = 6000, R = 10000, L = 250;
- int len[N], a[N], next[N];
- struct bignum
- {
- int a[L];
- int n;
- bignum operator = (int b)
- {
- memset(a,0,sizeof(a));
- n = 1;
- a[1] = b;
- }
- bignum operator = (const bignum &b)
- {
- memset(a,0,sizeof(a));
- for(int i = 1; i <= b.n; ++i) a[i] = b.a[i];
- n = b.n;
- }
- void print()
- {
- int i;
- printf("%d", a[n]);
- for(i = n-1; i >= 1; --i) printf("%04d", a[i]);
- }
- }cnt[N];
- bignum operator + (bignum &a, bignum &b)
- {
- bignum c;
- int i, len=max(a.n,b.n);
- c = 0;
- for(i = 1; i <= len; ++i)
- {
- c.a[i] += a.a[i] + b.a[i];
- if(c.a[i] >= R)
- {
- c.a[i+1] += c.a[i]/R;
- c.a[i] -= R;
- }
- }
- if(c.a[len+1]!=0) c.n=len+1;
- else c.n=len;
- return c;
- }
- int main()
- {
- int i, j, k, t, n;
- freopen("buylow.in","r",stdin);
- freopen("buylow.out","w",stdout);
- scanf("%d", &n);
- for(i = 1; i <= n; ++i) scanf("%d",a+i);
- a[++n] = 0;
- for(i = 1; i <= n; ++i)
- {
- for(j = i+1; j <= n; ++j)
- {
- if(a[i]==a[j])
- {
- next[i] = j;
- break;
- }
- }
- }
- for(i = 1; i <= n; ++i)
- {
- len[i] = 1;
- cnt[i] = 1;
- for(j = 1; j < i; ++j)
- {
- if(next[j] && next[j] < i) continue;
- if(a[i] < a[j])
- {
- if(len[j]+1 > len[i])
- {
- len[i] = len[j]+1;
- cnt[i]=0;
- cnt[i]=cnt[j];
- }
- else if(len[j]+1 == len[i])
- {
- cnt[i]=cnt[i]+cnt[j];
- }
- }
- }
- }
- printf("%d ", len[n]-1);
- cnt[n].print();
- puts("");
- return 0;
- }