usaco 4.3 Buy Low, Buy Lower
http://train.usaco.org/usacoprob2?a=7BaJNgFLmpD&S=buylow
求最长递减子序列以及方案数,注意重复不算,比如 3 2 3 2 1 ,这里取到最长递减子序列算一种(3 2 1)。
思路:
最长递减子序列的长度可以直接dp: dp[i]=max(dp[j]+1) (j<i&&a[j]<a[i])。
而方案数,如果不重复的话,在dp的过程中记录下就可以求了,设cnt[i]为以a[i]为结尾的方案数,那么推完dp的值之后再推一次,当dp[j]+1==dp[i](j<i)时,cnt[i]=cnt[j]。
然而这里要求重复,那么问题就来了,如何判重,避开重复。
思路是这样的:
对于序列........a[j]......a[i]......,如果a[i]==a[j],那么cnt[j]必然<=cnt[i],而且以a[j]为结尾的最有序列一定是以a[i]为结尾的最优序列的子集。
知道这一点之后思路就很简单了,计算cnt[i]时,遍历j=1~i,对于某个j,如果存在一个k,使j<k<i且a[k]==a[j],那么就不用把cnt[j]加进来了,这样就可以避免重复了。
最后一个问题,这里要用高精度,自己写了个重载高精度,感觉还行。
/* ID: huanrui ke PROG: buylow LANG: C++ */ #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=5010; const int INF=1e9+10; int n; ll a[maxn]; int dp[maxn],Next[maxn]; struct BigNum { int a[110],len; void init() { MS0(a);len=0; } void eq(ll x) { init(); while(x){ a[++len]=x%10; x/=10; } } /* friend BigNum operator=(BigNum A,ll x) { A.init(); while(x){ A.a[++len]=x%10; x/=10; } return A; } */ friend BigNum operator+(BigNum A,BigNum B) { BigNum C;C.init(); C.len=max(A.len,B.len)+1; int tag=0; REP(i,1,C.len){ C.a[i]=A.a[i]+B.a[i]+tag; tag=C.a[i]/10; C.a[i]%=10; } bool flag=0; for(int i=C.len;i>=1;i--){ if(C.a[i]){ flag=1; C.len=i;break; } } if(!flag) C.len=1; return C; } void Print() { if(len==0) len=1; for(int i=len;i>=1;i--) printf("%d",a[i]); } };BigNum cnt[maxn]; BigNum add(BigNum A,BigNum B) { return A+B; } int main() { #define ONLINE_JUDGE #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); #else freopen("buylow.in","r",stdin); freopen("buylow.out","w",stdout); #endif while(cin>>n){ REP(i,1,n) scanf("%d",&a[i]); a[++n]=0; memset(Next,-1,sizeof(Next)); map<ll,int> mp; for(int i=n;i>=1;i--){ if(mp[a[i]]) Next[i]=mp[a[i]]; else Next[i]=-1; mp[a[i]]=i; } REP(i,1,n){ dp[i]=1; REP(j,1,i-1){ if(a[j]>a[i]){ dp[i]=max(dp[i],dp[j]+1); } } } REP(i,1,n) cnt[i].init(); REP(i,1,n){ if(dp[i]==1){ cnt[i].eq(1); continue; } REP(j,1,i-1){ if(a[j]>a[i]){ if(dp[j]+1==dp[i]){ if(Next[j]!=-1&&Next[j]<i) continue; cnt[i]=add(cnt[i],cnt[j]); } } } } //REP(i,1,n) cout<<dp[i]<<" ";cout<<endl; //REP(i,1,n) cout<<cnt[i]<<" ";cout<<endl; printf("%d ",dp[n]-1); cnt[n].Print();puts(""); } return 0; } /** 6 3 3 4 2 2 1 6 3 4 2 3 2 1 1 3 5 3 3 3 2 2 6 4 3 2 5 3 2 */
没有AC不了的题,只有不努力的ACMER!