Matches UVA - 11375 递推+大数 Ingenuous Cubrency UVA - 11137 递推

这个题有意思。这种递推真的有点难想
题意:给你n个火柴棍,问你可以组成多少种非负的数字,没有前导零。

c[i]:表示第i个数用多少根火柴
d[i]:表示用i根火柴可以组成多少种数
状态转移方程为
d[i+c[j]]+=d[i];

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>
#define PLUS 1
#define MINUS -1
using namespace std;
const int maxn = 2005;
#define LL long long

int c[10]={6,2,5,5,4,5,6,3,7,6};
// 大数类。
class integer
{
    friend ostream& operator<<(ostream&, const integer&);
    friend istream& operator>>(istream&, integer&);

    friend int compare(const integer, const integer);
    friend bool operator<(const integer, const integer);
    friend bool operator<=(const integer, const integer);

    friend integer operator<<(integer, int);

    friend integer operator+(integer, integer);
    friend integer operator-(integer, integer);
    friend integer operator*(integer, integer);
    friend integer operator/(integer, integer);
    friend integer operator^(integer, int);

public:
    integer() { };

    integer(int a)
    {
        signbit = (a >= 0 ? PLUS : MINUS);
        a = abs(a);
        while (a)
        {
            digits.push_back(a % 10);
            a /= 10;
        }
    };

    ~integer() { };

    void zero_justify(void);

private:
    // 表示大数的结构。数位按低位到高位的顺序存放。存储的是数位值,而不是数位的字符值。
    // 数字 1 用 ASCII 码为 1 的字符,而不是 ‘1’ 来表示。
    vector < char > digits;   // 存放数位值。
    int signbit;        // 符号位。
};

integer operator+(integer, integer);
integer operator-(integer, integer);

// 重载输出符号<<。
ostream& operator<<(ostream& os, const integer& n)
{
    // 当为自然数时,不输出“+”号。
    os << (n.signbit > 0 ? "" : "-");
    if(n.digits.size()==0) cout<<0;
    for (int i = n.digits.size() - 1; i >= 0; i--)
        // 存放的是数位值,输出时要转换为数位的字符值。
        os << (char)('0' + n.digits[i]);
    return os;
}

// 重载输入符号>>。
istream& operator>>(istream& in, integer& n)
{
    // 读入表示大整数的字符串。
    string s;
    in >> s;

    if (s.length() == 0)
    {
        // 若长度为0,表示读入的是一个空字符串,应该要给予错误提示,但对于本题来说,不
        // 存在这种情况,故不考虑。若要完善,需要进一步处理此种情况。
    }

    // 在解析之前,理应判断该字符串是否是一个合法的大数表示,即开始一位为符号位,为“+”或
    // “-”,正整数“+”可以忽略,后面应该全部是数字字符,且不包含前导0。由于本题不存在此种情
    // 况,故不考虑,若要完善需进一步处理此种情况。

    // 解析符号位。
    if (isdigit(s[0]))
        n.signbit = PLUS;
    else
    {
        n.signbit = (s[0] == '+' ? PLUS : MINUS);
        s.erase(s.begin());
    }

    // 解析数字位,从低位到高位存放于动态数组中。
    n.digits.clear();
    for (int i = s.length() - 1; i >= 0; i--)
        n.digits.push_back((char)(s[i] - '0'));

    return in;
}

// 移除大数运算产生的前导0。
void integer::zero_justify(void)
{
    for (int i = digits.size() - 1; i >= 1; i--)
        if (digits[i] == 0)
            digits.erase(digits.begin() + i);
        else
            break;

    if (digits.size() == 1 && digits[0] == 0)
        signbit = PLUS;
}

// 比较两个数的大小,若相等,返回 0,若 a 大于 b,返回 1,若 a 小于 b,返回 -1。
int compare(const integer a, const integer b)
{
    // 若 a 为正,b 为负,则有 a 大于 b。
    if (a.signbit == PLUS && b.signbit == MINUS)
        return PLUS;

    // 若 a 为负,b 为正,则 a 小于 b。
    if (a.signbit == MINUS && b.signbit == PLUS)
        return MINUS;

    // 若两数符号相同,则 a 的数位长度大于 b 的数位长度,若 a 为正数,则有 a 大于 b,若 a
    // 为负数,有 a 小于 b,则将 a 的符号位乘以1作为结果返回即可。
    if (a.digits.size() > b.digits.size())
        return PLUS * a.signbit;

    // 若两数符号相同,若a的数位长度小于b的数位长度,若 a 为正数,则 a 小于 b,若 a 为负数,
    // 则 a 大于 b,则将 a 的符号位乘以 -1 作为结果返回即可。
    if (a.digits.size() < b.digits.size())
        return MINUS * a.signbit;

    // 两个数的数位长度相等,符号相等,则从低位到高位逐个比较数位值的大小。
    int marker = a.digits.size() - 1;
    while (marker >= 0)
    {
        if (a.digits[marker] > b.digits[marker])
            return PLUS * a.signbit;

        if (a.digits[marker] < b.digits[marker])
            return MINUS * a.signbit;

        marker--;
    }

    return 0;
}

// 小于比较符。
bool operator<(const integer a, const integer b)
{
    return compare(a, b) < 0;
}

// 小于等于比较符。
bool operator<=(const integer a, const integer b)
{
    return compare(a, b) <= 0;
}

// 左移操作。b 表示左移的位数。即将 a 乘以 10^b。
integer operator<<(integer a, int b)
{
    integer c = a;

    // 若 a 等于 0,则移位后仍为 0。
    if (a.digits.size() == 1 && a.digits[0] == 0)
        return c;

    // 向左移动 b 位,相当于在数位数组前插入 b 个 0。
    for (int i = 0; i < b; i++)
        c.digits.insert(c.digits.begin(), (char)(0));

    return c;
}

// 加法运算。从低位往高位进行,每一位相加产生的进位向高位传递,注意负数的处理,可以相应的
// 转变为减法来简化操作。
integer operator+(integer a, integer b)
{
    integer c;

    // 两数符号位相等,则最终符号位和原来的加数符号位相同。
    if (a.signbit == b.signbit)
        c.signbit = a.signbit;
    else
    {
        // 若两个加数为一正一负,则将其转换为减法操作。
        if (a.signbit == MINUS)
        {
            a.signbit = PLUS;
            c = b - a;
        }
        else
        {
            b.signbit = PLUS;
            c = a - b;
        }

        return c;
    }

    // 从动态数组的开始即低位开始相加,逐渐往前进位,如果有的话。
    int carry = 0;      // 进位。
    int maxsize = max(a.digits.size(), b.digits.size());
    int marker = 0;

    // 为两数添加前导 0,以使得数位相同,便于计算。
    while (a.digits.size() < maxsize)
        a.digits.push_back(char(0));
    while (b.digits.size() < maxsize)
        b.digits.push_back(char(0));

    // 逐位相加。
    while (marker < maxsize)
    {
        int t = a.digits[marker] + b.digits[marker] + carry;
        carry = t / 10;
        c.digits.push_back((char)(t % 10));

        marker++;
    }

    // 若最后一次进位为 1,则需在最高位加 1。
    if (carry)
        c.digits.push_back((char)(1));

    c.zero_justify();

    return c;
}

// 减法。为了保证借位能够成功终止,最好确保被减数不小于减数。同样可以在适当情况转变为加法来简化操作。
integer operator-(integer a, integer b)
{
    integer c;

    // 当某一个数为负数时,转换为加法。
    if (a.signbit == MINUS || b.signbit == MINUS)
    {
        b.signbit = MINUS * b.signbit;
        c = a + b;

        return c;
    }

    // 两个都为正数,若 a < b,则尽量保证被减数大于减数。
    if (a < b)
    {
        c = b - a;
        c.signbit = MINUS;

        return c;
    }
    else
        c.signbit = PLUS;

    int borrow = 0;     // 借位。
    int maxsize = a.digits.size();  // 被减数大于减数。
    int marker = 0;     // 计数器。

    // 为减数添加前导 0,便于计算。因为先前已经使得 a > b 且 a 和 b 均为正数。
    while (b.digits.size() < maxsize)
        b.digits.push_back(char(0));

    // 从低位开始逐位相减,不够的向高位借位。
    while (marker < maxsize)
    {
        int v = a.digits[marker] - borrow - b.digits[marker];
        if (v < 0)
        {
            v += 10;
            borrow = 1;
        }
        else
            borrow = 0;

        c.digits.push_back((char)(v % 10));

        marker++;
    }

    c.zero_justify();

    return c;
}

integer d[maxn];
integer ans[maxn];


int main()
{
    int n;
    d[0]=1;
    for(int i=1;i<=2000;i++)
        d[i]=0;
    for(int i=0;i<=2000;i++)
    {
        for(int j=0;j<10;j++)
          if(!(i==0&&j==0))
            d[i+c[j]]=d[i+c[j]]+d[i];
    }
    ans[0]=0;
    for(int i=1;i<=2000;i++){
        ans[i]=ans[i-1]+d[i];
    }
    for(int i=6;i<=2000;i++)
        ans[i]=ans[i]+1;
    while(scanf("%d",&n)!=EOF)
    {
        cout<<ans[n]<<endl;
    }
    return 0;
}

上面一道题中 123和321是不同的,但是在 Ingenuous Cubrency UVA - 11137
中这种组合是相同的。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>

using namespace std;
#define LL long long
const int maxn = 40;
int c[maxn];
LL dp[10005];
int main()
{
    int h=0;
    for(int i=1;i<30;i++)
    {
        h=i;
        int t=i*i*i;
        if(t>10000) break;
        c[i]=t;
    }
    dp[0]=1;
    for(int k=1;k<h;k++)
    {
        for(int i=c[k];i<=10000;i++)
            dp[i]+=dp[i-c[k]];
    }
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0) printf("0\n");
        else printf("%lld\n",dp[n]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值