天天看點

USACO4.3.1--Buy Low, Buy Lowerchunlvxiong的部落格題目描述:思考&分析:

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;
}      

轉載于:https://www.cnblogs.com/chunlvxiong/p/7436214.html