PC/UVa:110603/10198
给一个输入n,找出有多少种使用1、2、3、4的排列,使得和为n。特别的,这里的1和4是相同的,都当做1用,比如1 + 2 + 3 = 6,而4 + 2 + 3 = 6。假如输入是4,则排列方式有13、112、22、1111、121、211、31这么多种,又因为1和4都代表1,所以总的排列方式为2 + 4 + 1 + 16 + 4 + 4 + 2 = 33种。
写之前先在uDebug上试了一下输入是1000的时候结果有过大,结果很大,所以这题肯定得是高精度了。
然后就想可不可以直接计算。按照搜索的方式可以把所有可能的排列算出来,按照1、2、3扩展的方式,会得到排列1111、112、121、13、211、22、31。在n特别大时,这一次的搜索就用去了很长时间了,然后再根据1和4等价的条件,进行高精度运算,超时是没得说了,所以这题应该空间换时间。
DP中有一个上台阶的题目,是说第n级台阶可以通过n - 1级上1步得到,也可以通过n - 2级一次上2步得到,也可以通过n - 3级一次上3步得到。这道题可以通过将1、2、3加入到已有排列的末尾得到新的排列,所以递推公式就是F(n) = F(n - 3) + F(n - 2) + 2 * F(n - 1)。
代码里边有一个add2()函数,每次两个数相加,4个数要加3次,也就是3倍的复杂度。这么运行的话建表的时间大概8秒,肯定会超时了,然后就又写了一个add4()函数,4个相加只加1次,建表时间大概2秒。因为参数中保证了str1.size() <= str2.size() <= str3.size() == str4.size(),所以肯定是str1先退出,然后是str2。
#include <iostream>
#include <string>
#include <vector>
#define N 1000
using namespace std;
string add2(const string &str1, const string &str2)
{
size_t i = 0, j = 0;
string strRet;
int carry = 0, sum = 0;
while (i < str1.size() && j < str2.size()){
sum = str1[i] - '0' + str2[j] - '0' + carry;
if (sum >= 10){
sum -= 10;
carry = 1;
}
else carry = 0;
strRet.push_back(sum + '0');
i++, j++;
}
while (i < str1.size()){
sum = str1[i] - '0' + carry;
if (sum >= 10){
sum -= 10;
carry = 1;
}
else carry = 0;
strRet.push_back(sum + '0');
i++;
}
while (j < str2.size()){
sum = str2[j] - '0' + carry;
if (sum >= 10){
sum -= 10;
carry = 1;
}
else carry = 0;
strRet.push_back(sum + '0');
j++;
}
if (carry == 1) strRet.push_back('1');
return strRet;
}
string add4(const string &str1, const string &str2, const string &str3, const string &str4)
{
size_t i1 = 0, i2 = 0, i3 = 0, i4 = 0;
string strRet;
int carry = 0, sum = 0;
while (i1 < str1.size()
&& i2 < str2.size()
&& i3 < str3.size()
&& i4 < str4.size()){
sum = str1[i1] - '0' + str2[i2] - '0' + str3[i3] - '0' + str4[i4] - '0' + carry;
carry = sum / 10;
sum = sum % 10;
strRet.push_back(sum + '0');
i1++, i2++, i3++, i4++;
}
while (i2 < str2.size()
&& i3 < str3.size()
&& i4 < str4.size()){
sum = str2[i2] - '0' + str3[i3] - '0' + str4[i4] - '0' + carry;
carry = sum / 10;
sum = sum % 10;
strRet.push_back(sum + '0');
i2++, i3++, i4++;
}
while (i3 < str3.size()
&& i4 < str4.size()){
sum = str3[i3] - '0' + str4[i4] - '0' + carry;
carry = sum / 10;
sum = sum % 10;
strRet.push_back(sum + '0');
i3++, i4++;
}
while (i4 < str4.size()){
sum = str4[i4] - '0' + carry;
carry = sum / 10;
sum = sum % 10;
strRet.push_back(sum + '0');
i4++;
}
if (carry > 0) strRet.push_back(carry + '0');
return strRet;
}
void build(vector<string> &vecstrTbl)
{
string str;
for (size_t i = 3; i < N; i++)
{
str = add4(vecstrTbl[i - 3], vecstrTbl[i - 2], vecstrTbl[i - 1], vecstrTbl[i - 1]);
/*
str = vecstrTbl[i - 3];
str = add2(str, vecstrTbl[i - 2]);
str = add2(str, vecstrTbl[i - 1]);
str = add2(str, vecstrTbl[i - 1]);
*/
vecstrTbl.push_back(str);
}
}
int main()
{
int n = 0;
vector<string> vecstrTbl;
vecstrTbl.push_back("2");
vecstrTbl.push_back("5");
vecstrTbl.push_back("31");
build(vecstrTbl);
while (cin >> n){
cout << string(vecstrTbl[n - 1].rbegin(), vecstrTbl[n - 1].rend()) << endl;
}
return 0;
}
/*
2
3
*/
本文探讨了如何解决一个特定的计数问题,即找出使用1、2、3、4的排列,使得和为给定数值n的方法总数,其中1和4视为相同。通过动态规划(DP)的方法,构建了一个高精度的解决方案,避免了直接搜索的超时问题,并详细介绍了DP递推公式和高精度加法函数的实现。
227

被折叠的 条评论
为什么被折叠?



