这个题有意思。这种递推真的有点难想
题意:给你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]);
}
}