• USACO buylow


      这个题的意思就是求出最长递减子序列的长度以及数量, 第一问很好求, 第二问要求得话会比较麻烦,我们令dp[i]为以i结尾的最长递减子序列的长度,dp1[i]为以i为结尾长度为dp[i]的子序列的个数,因为题意还要求子序列不能重复,现在思考不重复时的情况, 不重复的时候dp1[i]的计算方法是这样的,当dp[i]==1的时候dp1[i]=1, 不等于1的时候自然等于dp1[j]的和, 其中dp[i] = dp[j]+1, a[j]>a[i], 考虑到题意求解不重复的方案数, 因此j转移到i时a[j]不应该有重复,例如5 5 3, 以3结尾的长度为二的子序列个数为1个, 前面两个5里面应该只能选一个,并且应该选后面那个。代码如下:

    /*
        ID: m1500293
        LANG: C++
        PROG: buylow
    */
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <set>
    #include <iostream>
    #include <vector>
    
    using namespace std;
    typedef long long LL;
    struct BigInteger
    {
        static const int BASE = 100000000;    //八位为一基本单位
        static const int WIDTH = 8;
        vector<int> s;
    
        BigInteger(long long num=0) { *this = num; }
        BigInteger operator = (long long num)
        {
            s.clear();
            do
            {
                s.push_back(num%BASE);
                num /= BASE;
            }while(num > 0);
            return *this;
        }
        BigInteger operator + (const BigInteger& b) const
        {
            BigInteger c;
            c.s.clear();
            for(int i=0, g=0; ;i++)
            {
                if(g==0&&i>=s.size() && i>=b.s.size()) break;
                int x = g;
                if(i < s.size()) x+=s[i];
                if(i < b.s.size()) x += b.s[i];
                c.s.push_back(x%BASE);
                g = x / BASE;
            }
            return c;
        }
        BigInteger operator += (const BigInteger& b)
        {
            *this = *this + b;
            return *this;
        }
        bool operator < (const BigInteger& b) const
        {
            if(s.size() != b.s.size()) return s.size() < b.s.size();
            for(int i=s.size()-1; i>=0; i--)
                if(s[i]!=b.s[i]) return s[i]<b.s[i];
            return false;
        }
        bool operator == (const BigInteger& b) const
        {
            return !(b<*this)&&!(*this<b);
        }
        void print()
        {
            printf("%d", s[s.size()-1]);
            for(int i=s.size()-2; i>=0; i--)
                    printf("%08d", s[i]);
            printf("
    ");
        }
    };
    int n;
    LL a[5500];
    int dp[5500];    //以i为结尾最长下降序列的长度
    BigInteger dp1[5500];
    
    
    
    int main()
    {
        freopen("buylow.in", "r", stdin);
        freopen("buylow.out", "w", stdout);
        scanf("%d", &n);
        for(int i=1; i<=n; i++)
            cin>>a[i];
        dp[1] = 1;
        dp1[1] = 1;
        int res1 = 1;
        set<LL> tp;
        for(int i=2; i<=n; i++)
        {
            tp.clear();
            dp[i] = 1;
            for(int j=1; j<i; j++)
                if(a[i]<a[j]) dp[i] = max(dp[i], dp[j]+1);
            res1 = max(res1, dp[i]);
    
            if(dp[i]==1)
                dp1[i] = 1;
            else
            {
                for(int j=i-1; j>=1; j--)                //这边注意循环方向 因为可能有a, a, b这种子序列的存在, 查了好久才查出来
                    if(dp[i]==dp[j]+1 && a[i]<a[j] && tp.find(a[j])==tp.end())
                    {
                        dp1[i] += dp1[j];
                        tp.insert(a[j]);
                    }
            }
        }
        BigInteger res2 = 0;
        tp.clear();
        for(int i=n; i>=1; i--)               //注意对于相同的数字选择下标最大的那个, 因为下标最大的子序列包括下标较小的子序列。
        {
            if(dp[i]==res1 && tp.find(a[i])==tp.end())
            {
                res2 += dp1[i];
                tp.insert(a[i]);
            }
        }
        printf("%d ", res1);
        res2.print();
        return 0;
    }
  • 相关阅读:
    【2021-04-15】台上一分钟,背后十年功
    【2021-04-14】买了人生中的第一个车位
    计算机网络实验部分
    乘积最大子数组
    计算各个位数不同的数字个数
    周总结
    最长回文子序列
    最长回文串
    回文子串
    拼图
  • 原文地址:https://www.cnblogs.com/xingxing1024/p/5164704.html
Copyright © 2020-2023  润新知