Buy Low, Buy Lower[USACO]

这道题的第一问比较简单,以a[i]结尾的最长减数列的长度f(i) = max{f(j)| j < i, a[j] > a[i]} + 1。

第二问,可以先考虑简单一点的,不考虑重复的话,以a[i]结尾的最长减数列的个数 g(i) = ∑{g(j)| j < i, a[j] > a[i], f(j) = f(i) - 1},我们要做的只是从中去除重复的情况,考虑一个例子:

.......64(1)......64(2)....a[i](小于64)....

64(1) 之前长度为L的减数列,将其与64(2)组合肯定还是一个减数列,而且64(2)为结尾的减数列最长长度肯定是 不小于L的,也就是说,此时计算g(i)时,就不需要64(1)不需要参与∑运算。怎么表示这种情况呢:

为了避免每次都扫描重复情况,只扫描一次,生成arrNextPos[],arrNextPos[j]记录着下一个a[j]的出现位置,如果这个位置小于i,则不考虑这个元素.

另外:

1.USACO好像没有_itoa,_atoi 这种东西,怎么整都是compile error。于一,第一次使用stringstream,感觉very good。

2.开始用的int,test7还是8来着 出错了,必须用Big Number。

3.用USACO的思想,2^31是个10位数,所以我把string 分割成8位一组来计算加法。需要注意添0,以及最后去掉头部多余的0。比如计算过程中的 100000001,我们将开头的1进位,后边取余时得到的1在搞成字符串的时候要把7个0加上。

这么计算加法的话,应该是比逐位计算快很多的~

 

 

/*
ID: zhangyc1
LANG: C++
TASK: buylow
*/
#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <iomanip>
using namespace std;

stringstream os;
class LongInt
{
public:
    LongInt(){strNum = "0";}
    LongInt(int i){os << i;os >> strNum;os.clear();}
    LongInt& operator=(int i){os << i;os >> strNum;os.clear();return *this;}
    LongInt& operator=(LongInt& other){strNum=other.strNum; return *this;}
    LongInt& operator+=(LongInt& other);
private:
    string strNum;
    friend ostream& operator<<(ostream& out, LongInt& longInt){out << longInt.strNum; return out;}
};

ofstream fileout("buylow.out");
const int MAX_SIZE = 5001;
// arrLen[i]:以a[i]结尾的最长减数列的长度; arrCount[i]:以a[i]结尾的最长减数列的个数
int N, arrNum[MAX_SIZE], arrLen[MAX_SIZE];
LongInt arrCount[MAX_SIZE];
// arrRepeated[i]:从a[i + 1]到a[N]是否已经出现过a[i]
int arrNextPos[MAX_SIZE];

LongInt& LongInt::operator+=(LongInt& other)
{
    // 2147483647 = 2^31 - 1。每次计算8位
    string strRs = "", strTemp;
    int nNum1, nNum2, nCarry = 0;
    int nLen1 = this->strNum.length();
    int nLen2 = other.strNum.length();
    while (nLen1 || nLen2 || nCarry)
    {
        if (nCarry == 0)
        {
            if (nLen1 <= 0)
            // 将剩余的字符串塞到strRs头中
            {
                string strLeft = other.strNum.substr(0, nLen2);
                strRs.insert(0, strLeft);
                break;
            }
            if (nLen2 <= 0)
            // 将剩余的字符串塞到strRs头中
            {
                string strLeft = this->strNum.substr(0, nLen1);
                strRs.insert(0, strLeft);
                break;
            }
        }

        nNum1 = nNum2 = 0;
        int nLen = 0;
        if (nLen1 > 0)
        {
            if (nLen1 > 8)
                nLen = 8;
            else
                nLen = nLen1;
            nLen1 = nLen1 - nLen;
            os << strNum.substr(nLen1, nLen);
            os >> nNum1;
            os.clear();
        }
        if (nLen2 > 0)
        {
            if (nLen2 > 8)
                nLen = 8;
            else
                nLen = nLen2;
            nLen2 = nLen2 - nLen;
            os << other.strNum.substr(nLen2, nLen);
            os >> nNum2;
            os.clear();
        }
        int rs = nNum1 + nNum2 + nCarry;
        nCarry = rs / 100000000;
        rs %= 100000000;
        os << setfill('0') << setw(8) << rs;
        os >> strTemp;
        os.clear();
        strRs.insert(0, strTemp);
    }
    int nPos = 0;
    while (strRs[nPos] == '0' && nPos < strRs.length())
    {
        nPos++;
    }
    if (nPos == strRs.length())
    {
        nPos = strRs.length() - 1;
    }
    this->strNum = strRs.substr(nPos);
    return *this;
}

void prepairData()
{
    ifstream filein("buylow.in");
    filein >> N;
    for (int i = 0; i < N; i++)
    {
        filein >> arrNum[i];
    }
    arrLen[0] = 1;
    arrCount[0] = 1;
    arrNum[N] = 0;

    // 记录出现过的数
    map<int, int> mapAppear;
    map<int, int>::const_iterator mapIt;
    for (int i = N; i >= 0; i--)
    {
        mapIt = mapAppear.find(arrNum[i]);
        if (mapIt == mapAppear.end())
        {
            arrNextPos[i] = MAX_SIZE;
        }
        else
        {
            arrNextPos[i] = mapIt->second;
        }
        mapAppear[arrNum[i]] = i;
    }
    filein.close();
}

void process()
{
    // 计算最长长度
    for (int i = 1; i <= N; i++)
    {
        int nMax = 0;
        for (int j = 0; j < i; j++)
        {
            if (arrNum[i] < arrNum[j] && nMax < arrLen[j])
            {
                nMax = arrLen[j];
            }
        }
        arrLen[i] = nMax + 1;
    }
    // 计算以各元素结尾的最长减数列的个数
    for (int i = 1; i <= N; i++)
    {
        if (arrLen[i] == 1)
        {
            arrCount[i] = 1;
            continue;
        }

        LongInt nSum = 0;
        for (int j = 0; j < i; j++)
        {
            if (arrLen[i] == arrLen[j] + 1 && arrNum[j] > arrNum[i] && arrNextPos[j] > i)
            {
                nSum += arrCount[j];
            }
        }
        arrCount[i] = nSum;
    }
    fileout << arrLen[N] - 1 << " " << arrCount[N] << endl;
}

int main(){
    prepairData();
    process();
    fileout.close();    
    return 0;
}

  

 

 

posted @ 2013-04-02 09:16  J.Z's World  阅读(225)  评论(0编辑  收藏  举报