您的位置:首页 > 其它

算法学习笔记(三)问题的转化与高精度运算

2014-09-25 11:53 274 查看
问题:设购票点没有任何的零钱,票价50美元,现有m人手持50美元,n人手持100美元,求这样m+n个人构成的队伍有多少种排队方法可以使得整个售票过程不中断。

分析:对于这个问题,经过简单的模拟可以发现,每个手持100的前面必须有一个手持50的,同样如果有k个手持100的连续出现,则前面至少连续k次50。
这样一来,可以设手持50元的为+1,手持100元的为-1,设ai为为第i个人所对应的值,则问题转化为数组的部分和a1+a2+...+ak≥0,其中k≤m+n,为了求这样的数列的个数,需要使用组合数学的相关知识,这个问题可以使用现成的结论,第n个Catalan数,公式为:



在运算时,要特别注意m<n的情况,在这种情况下,无法满足要求。

算法实现:由于购票人数一般很多,这就带来了高精度运算的问题,不再能使用传统的运算方式。高精度运算的一种方法是把数据都转化为字符串,再对字符串定义运算。
①将数据转化为字符串
将数据转化为字符串的算法是十分有效的,尤其在单片机的编程当中,一般的算法是将整数从高位到低位顺次存入一个字符数组,这时字符数组所存的数据是反的,这时候需要再设定一个数组将其反过来,通过查阅资料,发现了一种比较号的算法,它的亮点是①充分利用i++的先运算后++特性简化代码,②省略对位数的判断,而采用while(n)判断所有位数是不是都已经取完,③在字符数组尾部赋0从而保证字符串在最后一个有效位后结束。代码如下:
void NumToString(int n, char* s)
{
int i,j,temp[8]; //临时数组,先把数位存入其中,倒序
if(n == 0)
{
s[0] = '0';
s[1] = 0; //在有效位之后添加\0
return;
}
i = 0;
while(n) //判断有没有取完所有位
{
temp[i++] = n % 10; //先存入,后i++
n /= 10;
}
i -= 1;
j = 0;
while(i >= 0)
s[j++] = temp[i--] + '0'; //将倒序的数组正序存入s数组,s数组存有最终结果
s[j] = 0;
}
②重新定义字符串的运算,以乘法为例,算法如下:
void mul(char* m, char* n, char* res)
{
int i,j,len1,len2;
len1 = strlen(m);
len2 = strlen(n);
int *r = new int[len1 + len2 + 1]; //乘积的长度,例如两位乘以两位,最多为5位,因此多加1
for(i = 0; i <= len1 + len2; i++) r[i] = 0; //初始化乘积存储数组
for(i = 0; i < len1; i++)
for(j = 0; j < len2; j++)
r[i + j + 1] += (m[i] - '0')*(n[j] - '0'); //依次从最高位、次高位直至最低位进行运算,注意,这里的最高位实际为次高位,因为
//真正的最高位只能通过进位获得,而不是通过乘运算
for(i = len1 + len2 - 1; i >=1; i--) //从最低位开始处理进位
{
if(r[i] > 9)
{
int temp = r[i] / 10; //储存进位数
r[i] %= 10; //将进位后的数组存在这一位
r[i-1] += temp; //进位运算,同时处理r[0]这一位(进位可能到达的最高位,没有则为0)
}
}
for(i = 0; i<len1+len2;i++) cout << r[i] << " ";
cout << endl;
//经过这样的运算,将会得到数据字符串,其中最高位在r[0]内,最低位在r[len1+len2-1]内
for(i = 0; i < len1+len2; i++) //判断是否乘积为0,为0则i会自加到len1+len2
if(r[i] != 0) break;
if(i == len1 + len2)
{
res[0] = '0';
res[1] = 0;
return;
}
//如果乘积不为0,会进行下面的运算,顺次将r[0]到r[len1+len2-1]存入res[0]到res[len1+len2-1]
j = 0;
while(i < len1 + len2)
{
res[j++] = r[i++] + '0';
}
res[j] = 0;
delete[] r;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息