Contest100000593 - 《算法笔记》5.6小节——数学问题->大整数运算

Contest100000593 - 《算法笔记》5.6小节——数学问题->大整数运算

5.6小节——数学问题->大整数运算

5.6.1 大整数的存储

//5.6.1大整数的存储
struct bign{
	int a[1000];
	int len;
};
//输入大整数时,先用字符串读入,然后把字符串另存为至bign结构体
bign change(char str[])
{
	bign a;
	a.len = strlen(str);//bign的长度就是字符串的长度
	for(int i = 0;i<a.len;i++){
		a.d[i] = str[a.len - i - 1] - '0';//字符串倒着赋值给d[]数组 
	} 
	return a;
}
//比较两个bign变量的大小,先判断len大小,若相等,则从高位到低位遍历
int compare(bign a,bign b)//比较a/b,a大、相等、a小分别返回1、0、-1 
{
	if(a.len > b.len)	return 1;//a大
	else if(a.len < b.len)	return -1;
	else
	{
		for(int i=a.len - 1;i >= 0;i--)//从高位往低位比较 
		{
			if(a.d[i] > b.d[i])	return 1;//只要有一位a大,则a大
			else if(a.d[i] < b.d[i]) return -1; 
		}	
		return 0;//两数相等 
	} 
}	 

5.6.2大整数的四则运算

1.高精度加法

代码
//1高精度加法
bign add(bign a,bign b)//高精度a+b 
{
	bign c;	
	int carry = 0;//carry是进位
	for(int i=0;i<a.len || i<b.len;i++)//以较长的为界限,从低到高位加 
	{
		int temp = a.d[i] + b.d[i] + carry;//两个对应位与进位相加
		c.d[c.len++] = temp % 10;//个数位为该位结果
		carry = temp / 10;//十位数为新的进位 
	} 
	if(carry != 0)//如果最后进位不为0,则直接赋值给结果的最高位
	{
		c.d[c.len++] = carry;	
	} 
	return c;
}	
	 
完整A+B代码(非负整数)
//完整A+B代码(非负整数) 
#include <cstdio>
#include <cstring>

struct bign{
	int d[1000];
	int len;
	bign(){
		memset(d,0,sizeof(d));
		len = 0;
	}
};

bign change(char str[])//将整数转换为bign
{
	bign a;
	a.len = strlen(str);
	for(int i=0;i<a.len;i++){
		a.d[i] = str[a.len - i - 1] - '0';
	}
	return a;
}

bign add(bign a,bign b){//高精度a+b
	bign c;
	int carry = 0;//carry是进位
	for(int i=0;i<a.len||i<b.len;i++){
		int temp = a.d[i] + b.d[i] + carry;
		c.d[c.len++] = temp%10;
		carry = temp / 10;
	}
	if(carry != 0){
		c.d[c.len++] = carry;
	}
	return c;
}
void print(bign a){//输出bign 
	for(int i=a.len - 1;i >= 0;i--){
		printf("%d",a.d[i]);
	}
}

int main()
{
	char str1[1000],str2[1000];
	scanf("%s%s",str1,str2);
	bign a = change(str1);
	bign b = change(str2);
	print(add(a,b));
	return 0;
}	
	 

2.高精度减法

代码
//2.高精度减法
bign sub(bign a,bign b){//高精度a-b
	bign c;
	for(int i=0;i<a.len || i<b.len;i++){//以较长的为界限
		if(a.d[i] < b.d[i]){//如果不够减
			a.d[i+1]--;//向高位借位
			a.d[i] += 10;//当前位加10 
		} 
		c.d[c.len++] = a.d[i] - b.d[i];//减法结果为当前位结果 
	}
	while(c.len-1 >= 1 && c.d[c.len - 1] == 0){
		//注意此处len-1,因为赋值时候用的c.len++,所以先赋值再++,数组最后一个数为c.d[len-1] 
		c.len--;//去除高位的0,同时至少保留一位最低位 
	}
	return c;
}	
	 

3.高精度与低精度的乘法

代码
//高精度与低精度的乘法:始终将低精度数作为一个整体看待
bign multi(bign a,bign b){//高精度乘法 
	bign c;
	int carry = 0;//进位
	for(int i = 0;i < a.len;i++){
		int temp = a.d[i] * b + carry;
		c.d[c.len++] = temp % 10;
		carry = temp / 10;
	}	

	while(carry != 0){//和加法不一样,乘法的进位可能不止一位,因此用while
		c.d[c.len++] = carry % 10;
		carry /= 10;
	}
	return c;
}	
	 

4.高精度与低精度的除法

代码
//4.高精度与低精度的除法(注意最后一步减法后高位可能有多余的0,
//要忽视它们,但也要保证结果至少有一位数
bign divide(bign a,int b,int &r){//高精度除法,r为余数
	bign c;
	c.len = a.len;//被除数的每一位和商的每一位是一一对应的,因此先令长度相等
	for(int i = a.len - 1;i >= 0;i--){//从高位开始 
		r = r * 10 + a.d[i];//和上一位遗留的余数组合
		if(r < b)	c.d[i] = 0;//不够除,该位为0
		else{//够除 
			c.d[i] = r/b;//商
			r = r % b;//获得新的余数 
		} 
	}
	while(c.len - 1 >= 1 && c.d[c.len - 1] == 0){
		c.len--;//去除高位的0,同时至少保留一位最低位 
	}
	return c; 
}
	
	 

Codeup习题

Contest100000593 - 《算法笔记》5.6小节——数学问题->大整数运算

1949-Problem-A-a+b

题目链接:http://codeup.cn/problem.php?cid=100000593&pid=0
//关于大整数与字符串的转化,字符串可以用char数组(C语言)与string(C++语言)
//之前print 返回类型错写成bign,结果程序死活过不去,后来改了就过了,真是玄学

//1949-Problem-A-a+b
//关于大整数与字符串的转化,字符串可以用char数组(C语言)与string(C++语言) 
//之前print 返回类型错写成bign,结果程序死活过不去,后来改了就过了,真是玄学 
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct bign{
	int data[1050];
	int len;
	bign(){
		memset(data,0,sizeof(data));
		len = 0;
	}
};

bign change(string str){
	bign a;
//	a.len = strlen(str);
	a.len = str.size();
	for(int i=0;i<a.len;i++){
		a.data[a.len - i - 1] = str[i] - '0';
	}
	return a;
} 

bign add(bign a,bign b){
	bign c;
	int carry=0;
	for(int i=0;i<a.len || i<b.len;i++){
		int temp = a.data[i] + b.data[i] + carry;
		c.data[c.len++] = temp % 10;
		carry = temp / 10;
	}
	if(carry != 0){
		c.data[c.len++] = carry;
	}
	return c;
}

void print(bign a){
	for(int i=a.len-1;i>=0;i--){
		printf("%d",a.data[i]);
	}
}

int main()
{
	string str1,str2;
	while(cin>>str1>>str2){
		bign a = change(str1);
		bign b = change(str2);
		print(add(a,b));
		cout<<endl;
	}
	return 0;
}	
	 

1917-Problem-B-N的阶乘

题目链接: http://codeup.cn/problem.php?cid=100000593&pid=1

//1917-Problem-B-N的阶乘
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

struct bign{
	int data[5000];
	int len;
	bign(){
		memset(data,0,sizeof(data));
		len = 0;
	}
};
bign change(string str){
	bign a;
//	a.len = strlen(str);
	a.len = str.size();
	for(int i=0;i<a.len;i++){
		a.data[a.len - i - 1] = str[i] - '0';
	}
	return a;
} 
bign multi(bign a,int b){//高精度乘法 
	bign c;
	int carry = 0;//进位
	for(int i = 0;i < a.len;i++){
		int temp = a.data[i] * b + carry;
		c.data[c.len++] = temp % 10;
		carry = temp / 10;
	}	

	while(carry != 0){//和加法不一样,乘法的进位可能不止一位,因此用while
		c.data[c.len++] = carry % 10;
		carry /= 10;
	}
	return c;
}
void print(bign a){
	for(int i=a.len-1;i>=0;i--){
		printf("%d",a.data[i]);
	}
}

int main(){
	int n;
	while(cin>>n){
		bign res;
		res.data[0]=1; //注意初始化为1
		res.len = 1;	
		for(int i=2;i<=n;i++)
		{
			res = multi(res,i);//注意函数返回类型,刚开始没有更新res,一直输出1 
		}
		print(res);
		cout<<endl;
	}
	return 0;
}	
	 

1922-Problem-C-浮点数加法

题目链接:http://codeup.cn/problem.php?cid=100000593&pid=2

//1922-Problem-C-浮点数加法
//仿照大整数思想,将浮点数与字符串转换,并将整数与小数部分分开存储与处理 
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct floatn{
	int x[1000];
	int y[1000];
	int px;//整数部分指针 
	int py;//小数部分指针 
	floatn(){
		memset(x,0,sizeof(x));
		memset(y,0,sizeof(y));
		px = 0;
		py = 0;
	}
};

floatn change(string str){//字符串浮点数存储到结构体相应数组中 
	floatn a;int flag = 1;
	a.px=0,a.py=0;//整数、小数部分指针 
	for(int i=0;i<str.length();i++){
		if(str[i] == '.'){
			flag = 0;
			continue;
		}
		if(flag)	a.px++;//整数部分 
		else	a.y[a.py++] = str[i] - '0';//小数部分正序存储 
	}
	for(int i=0;i<a.px;i++){//整数部分顺序存储 
		a.x[a.px - i - 1] = str[i] - '0';
	}
	return a;
} 
//浮点加法 
floatn add(floatn a,floatn b)//高精度a+b 
{
	floatn c;	
	int carry = 0;//carry是进位
	//小数部分相加
	c.py = max(a.py,b.py);//注意小数部分计数指针的处理 
	int temp0 = 0;
	for(int i=c.py-1;i>0;i--){
		int temp = a.y[i] + b.y[i] + carry;
		c.y[i] = temp % 10;//此处是c.y[i] 
		carry = temp / 10;
	}
	if(carry != 0)//如果最后进位不为0,则进位至小数位最高位y[0] 
	{
		temp0 = carry;	
	}
	//小数位次高位进位可能会影响小数位最高位和整数位最低位 
	carry= (a.y[0] + b.y[0] + temp0) / 10;
	c.y[0] += (a.y[0] + b.y[0] + temp0) % 10;
	
	while(!c.y[c.py-1])	c.py--;//小数部分坑:末尾相加为0不要输出,将py-- 
	
//	carry = 0;
	//整数部分相加 
	for(int i=0;i<a.px || i<b.px;i++){
		int temp = a.x[i] + b.x[i] + carry;
		c.x[c.px++] = temp % 10;
		carry = temp / 10;
	}
	if(carry != 0)//如果最后进位不为0,则直接赋值给结果的最高位
	{
		c.x[c.px++] = carry;	
	} 
	return c;
}

void print(floatn a){//输出浮点数,注意整数倒序存储,小数顺序存储 
	for(int i=a.px-1;i>=0;i--){
		printf("%d",a.x[i]);
	}
	printf(".");
	for(int i=0;i<a.py;i++){
		printf("%d",a.y[i]);
	}
}


int main(){
	int n;
	while(cin>>n){
		while(n--){
			string str1,str2;
			cin>>str1>>str2;
			floatn z;
			floatn x = change(str1);
			floatn y = change(str2); 
			z = add(x,y);
			print(z);
			cout<<endl;
		}
	}
	return 0;
}	
	 

1950-Problem-D-进制转换

题目链接: http://codeup.cn/problem.php?cid=100000593&pid=3

//1950-Problem-D-进制转换
//类似于辗转相除法将十进制转换为2进制,正序输入,除得余数倒序输出,注意乘以相应的权值
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	int M,N;
	string str;
	while(cin>>M>>N>>str){
		int in[1050],out[1050];//输入存放数组in,输出存放数组out 
		int len = str.length();
		for(int i=0;i<len;i++){//将输入的字符串转化为数存放 
			if(str[i] >= '0' && str[i] <= '9')	in[i] = str[i] - '0';
			else	in[i] = str[i] - 'A' + 10;
		}
		int index=0,sum=1;//数组下标指针与判停参数 
		while(sum){//辗转相除法获取out数组,期间将in[]数的还原和存储到out[]结合到一起了 
			sum = 0;
			for(int i=0;i<len;i++){
				sum += in[i] / N;//相除,更新sum,当in[]数组为0,sum为0停止
		//本次循环里最后一位权重为1;当i==len-1时,数据还原完成,开始辗转相除取余,存入out[] 
				if(i == len - 1)	out[index++] = in[i] % N;
				else in[i+1] += (in[i] % N) * M;//乘以相应权重加到下一位,还原in[]的数 
				in[i] /= N; 
			}
		}
		if(index == 0){
			cout<<0;
		} 
		else{
			for(int i=index-1;i>=0;i--){//倒叙输出输出数组 
				if(out[i] >= 0 && out[i] <= 9)
					cout<<out[i];
				else//注意字母的处理 
					cout<<(char)(out[i] + 'a' - 10);
			}
		}
		cout<<endl;
	}
	return 0;	
}	
	 

1951-Problem-E-大整数排序

题目链接: http://codeup.cn/problem.php?cid=100000593&pid=4

//1951-Problem-E-大整数排序
//有时候大整数直接等价于字符串处理,特别是string类型的出现又花了这一流程
//当然,输入输出必须是配套的C++流处理 
//bign此题并不合适,因为bign相当于string,但是这题是string数组 
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

bool cmp(string x,string y){//排序规则 
	if(x.length() != y.length())//先比较长度 
		return x.length() < y.length();
	else{
		for(int i=0;i<x.length();i++){//长度相等从高位向低位比较各位上的数 
			if(x[i] != y[i])	
				return x[i] < y[i];//相等时顺序不变 
		}
	}
}

int main()
{
	int N;
	string num[110];
	while(cin>>N){
		for(int i=0;i<N;i++){//输入多个大整数 
			cin>>num[i];
		}
		sort(num,num+N,cmp);
		for(int i=0;i<N;i++){//输出 
			cout<<num[i]<<endl;
		}
	}
	return 0;
}	
	 

1952-Problem-F-10进制-VS-2进制

题目链接: http://codeup.cn/problem.php?cid=100000593&pid=5
进制转换进阶题

//1952-Problem-F-10进制-VS-2进制
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

int main()
{
	string str;
	while(cin>>str){
		int in[1010],temp[5010],out[1010];
		int len = str.length();
		for(int i=0;i<len;i++){
			in[i] = str[i] - '0';
		}
		int index=0,sum=1;
		while(sum){//输入数据in[]转换为2进制temp[] 
			sum = 0;
			for(int i=0;i<len;i++){
				sum += in[i]/2;
				if(i == len-1)	temp[index++] = in[i]%2;
				else in[i+1] += (in[i]%2)*10;
				in[i] /= 2;
			}
		}
		if(index == 0){
			cout<<0;
		}
		else{//二进制temp转换为out,temp[]逆序存储,则按存储顺序处理即可得逆序数 
			len = index,index=0,sum=1;
			while(sum){
				sum=0;
				for(int i= 0;i<len;i++){
					sum += temp[i] / 10;
					if(i == len-1)	out[index++] = temp[i]%10;
					else temp[i+1] += (temp[i] % 10) * 2;
					temp[i] /= 10;
				}
			} 
			for(int i=index-1;i>=0;i--){
				cout<<out[i];
			}
		}
		cout<<endl;
	}
	return 0;
}	
	 

总结

大整数相当于一维的字符串问题,用C++的string类型与cin、cout简直很溜;bign大整数数据类型适用于一维特殊情况

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李霁明

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值