chunlvxiong的博客
题目描述:
给出N(1≤N≤5000)个数,要求一个子序列是递减的,求出这个子序列的最大长度s,和长度为s的子序列的个数(注意,如果两个序列的数值一模一样它们算同一种序列)。
思考&分析:
用a[i]表示第i个数。
第一问求s应该较好解决,使用DP求解,用dp[i]表示i作为子序列末尾的最大长度,则方程为:
dp[i]=max{dp[j]+1|1≤j<i且a[j]>a[i]}
然后ans1=max{dp[i]|1≤i≤n},时间复杂度O(N^2)。
第二问如果没有数值一样算同一种序列的限制,也比较好解决,同样使用DP求解,用f[i]表示i作为子序列末尾且该子序列长度最大的方案种数:
dp[i]==1:f[i]=1
dp[i]>1:f[i]=Σf[j](1≤j<i且a[j]>a[i]且dp[i]==dp[j]+1)
ans2=Σf[i](dp[i]==ans1)
问题在于所有数值一样算同一种序列,怎么避免这个问题呢?方法如下:
如果存在a[x]==a[y]且dp[x]==dp[y](y<x),那么f[i]=Σf[j]中j的范围仅限于y+1..x-1,因为1..y-1中的方案末尾放a[j]或是a[i]都是一样的,为了避免重复计算,我们将其归于a[j],从而a[i]计算范围减少。
对于dp[i]==1的,如果没有dp[j]==1(1≤j<i)且a[j]==a[i]的,那么f[i]=1,否则f[i]=0。
仍然有ans2=Σf[i](dp[i]==ans1)。
然而USACO出这个题的核心不在这儿……而是:由于N=5000,第二问的答案可能非常大,需要使用高精度。
所以总的时间复杂度为O(N^2*高精度复杂度)-->N=5000时非常容易T,为了保险我压了四位,在USACO上跑了0.266s。
贴代码:
#include <bits/stdc++.h> using namespace std; int n,a[5005],dp[5005]; struct gjd{ int num[25]; int &operator [] (int p) { return num[p]; } void clear(){ memset(num,0,sizeof(num)); } void set(int x){ clear(); num[0]=0; while (x){ num[++num[0]]=x%10; x/=10; } if (!num[0]) num[0]=1; } gjd operator +(gjd &b){ gjd c; c.clear(); c[0]=max(num[0],b[0]); for (int i=1;i<=c[0];i++){ c[i]+=num[i]+b[i]; c[i+1]+=c[i]/10000; c[i]%=10000; } while (c[c[0]+1]>0) c[0]++; return c; } void print(){ printf("%d",num[num[0]]); for (int i=num[0]-1;i>=1;i--){ if (num[i]<1000) putchar('0'); if (num[i]<100) putchar('0'); if (num[i]<10) putchar('0'); printf("%d",num[i]); } puts(""); } }num[5005],res; int main(){ freopen("buylow.in","r",stdin); freopen("buylow.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); int ans=0; for (int i=1;i<=n;i++){ dp[i]=1; for (int j=1;j<i;j++) if (a[i]<a[j]) dp[i]=max(dp[i],dp[j]+1); ans=max(ans,dp[i]); } printf("%d ",ans); res.set(0); for (int i=1;i<=n;i++){ if (dp[i]==1){ num[i].set(1); for (int j=i-1;j>=1;j--) if (a[i]==a[j] && dp[j]==1){ num[i].set(0); break; } } else{ num[i].set(0); for (int j=i-1;j>=1;j--){ if (a[i]==a[j] && dp[i]==dp[j]) break; if (a[i]<a[j] && dp[i]==dp[j]+1) num[i]=num[i]+num[j]; } } if (dp[i]==ans) res=res+num[i]; } res.print(); return 0; }