王道机试 数学篇

%运算符 与 数位拆解

  • r'=(r+b)%b 取模养成习惯,防止出现负值
  •  常用来防溢出,减法和除法呢?
  • 优先级和* /一样,so比+ -高

case1:1938: 还是A+B 非常简单,关键是知道:取到最后k位就是%10^k即可;case2:1939: 守形数 亦非常简单

case3:特殊乘法。可以直接用硬方法把数位一个个分解出来,也可以直接巧妙的存为string然后逐位-'0'后相乘即可!

#include<iostream>
#include<cmath>
using namespace std;
int a[15],b[15];
int F(int x,int m[]) {//或者写int *m也可以,&m不行。。。所以这里我还没搞懂啊
	int i=0;
	while(x>0) {
		m[i++]=x%10;
		x/=10;
	}
	return i;
}
int main() { //我17:08->17:16
	int x,y;//int 能存2e10,够用了
	while(cin>>x>>y) {
		int na=F(x,a);
		int nb=F(y,b);
		int sum=0;
		for(int i=0; i<na; i++)
			for(int j=0; j<nb; j++) {
				sum+=a[i]*b[j];
			}
		printf("%d\n", sum);
	}
	return 0;
}
//法2
int main() {
	string x,y;//int 能存2e10,够用了
	while(cin>>x>>y) {
		int sum=0;
		for(int i=0; i<x.length(); i++)
			for(int j=0; j<y.length(); j++) {
				sum+=(x[i]-'0')*(y[j]-'0');
			}
		printf("%d\n", sum);
	}
	return 0;
}

case4:对称平方数,输出指定范围内n^2是回文数的n,注意考虑0

case5:Digital Roots,关键都是读懂题意,39分为3+9=12>10,1+2=3<10所以39的root就是3,我首先想到的就是用递归,需要注意的就是递归的时候,要return,因为在最子层得到的值也是需要在上层递归函数里return 到main的,本地运行OK,可能是因为编译器的原因,但是在牛客OJ通过率却是0,这是法1:(程序里还体现了法3,通过数字根数学推导,直接(x+8)%9+1)

#include<iostream>
using namespace std;
int F(int x) {
	int ans=0;
	while(x) {
		ans+=x%10;
		x/=10;
	}
	if(ans<10) {
		return ans;
	} else return F(ans);//应该要写return,因为递归后的F,得来的值,还是要经过这里return回main。
		        	//但是不写为啥也能得到ans呢? 不过在牛客OJ通过率就为 0%了 
}
int main() { //我17:52->18:01
	int x;
	while(scanf("%d", &x)!=EOF) {
		printf("%d\n", F(x));
//		printf("%d\n", (x+8)%9+1);
	}
	return 0;
}

法2:还可以用循环实现递归,这里有点技巧了...因为我之前以为这不是直接将每位相加,是不是不Ok,但是也是AC的,这里的话运行就是eg 198=19+8=27>10,2+7=9<10,so ans=9,why也是正确的呢?【一般递归转非递归,需要一个栈来模拟?】

#include <iostream>
using namespace std;
int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        int a,b;
        while(n>=10){
            b = n%10;
            a = n/10;
            n = a+b;
        }
        cout<<n<<endl;
    }
    return 0;
}

进制转换

  • 10->k:对x不断%k再/k 直到为0,%运算依次的结果就是从低到高位的ans
  • k->10:将每位的数与k次幂相乘,最后在相加即可

case1:k进制的A+B,关键在于0的处理!弄错好几次了,要先do再while,不然0没有输出;另外因为是2^32-1的数量级,虽然int可以表示下,但是相加后会超出,so 用long long

#include <iostream>
using namespace std;
typedef unsigned long long LL;
int main() {
	LL a,b,k;//a,b是10进制
	while(cin>>k, k!=0) {
		cin>>a>>b;
		LL sum=a+b;
		string s;
		do {	//注意考虑0的问题! 
			s+=sum%k+'0';
			sum/=k;
		}while(sum>0);
		for(int i=s.length()-1; i>=0; i--)
			cout<<s[i];
		cout<<endl;		
	}
	return 0;
}

case2:数制转换,方法就是a->10->b进制(2<=a,b<=16),难点在于11到16进制的问题:a->10时,遇到字母时,用10+s[i]-'A'即可;10->b时,若sum%b>=10,则用 ans+='A'+sum%b%10即可

#include <iostream>
#include<cmath>
using namespace std;
typedef unsigned long long LL;
int main() {//16:53->17:22 用了29min
	int a,b;//a->b进制
	string n;
	while(cin>>a>>n>>b) {
		LL sum=0;
		for(int i=0; i<n.length(); i++) {
			if(n[i]>='0'&&n[i]<='9')
				sum+=(n[i]-'0')*pow(a,n.length()-i-1);
			else if(n[i]>='a'&&n[i]<='z') {
				sum+=(10+n[i]-'a')*pow(a,n.length()-i-1);
			} 
			else if(n[i]>='A'&&n[i]<='Z') {
				sum+=(10+n[i]-'A')*pow(a,n.length()-i-1);
			}
		}
//		cout<<sum<<endl;
		string ans;
		do {
			if(sum%b>=10)
				ans+='A'+sum%b%10;//加上尾数即可
			else ans+=sum%b+'0';
			sum/=b;
		} while(sum>0);
		for(int i=ans.length()-1; i>=0; i--)
			cout<<ans[i];
		cout<<endl;
	}
	return 0;
}

gcd与lcm

gcd(Greatest Common Divisor)原理就是利用欧几里得算法,将a与b不断转化为b与a%b,当b==0时的a就是gcd

#include <iostream>
using namespace std;
int gcd(int a, int b) { //递归法
	if(b==0) return a;
	else return gcd(b,a%b);
}
int main() {
	int a,b,t;//原理是a与b,转化为b与a%b
	while(cin>>a>>b) {
		while(b!=0) { //循环法
			t=a%b;
			a=b;
			b=t;
		}
//		cout<<a<<endl;
		cout<<gcd(a,b)<<endl;//递归法
	}
	return 0;
}

lcm(Lowest Common Multiple)就是 a * b  / gcd(a,b) 但是!编程时a*b可能溢出,so a / gcd(a,b) * b 

case1:多位数的最小公倍数,eg 3 5 17的LCM=105就是两两求lcm,新得到的lcm再与后面的数一起计算的lcm,直到算完,这里需要考虑只有一个数的情况!另外lcm要先除再乘,防溢出

#include <iostream>
using namespace std;
int gcd(int a, int b) { //递归法
	return b==0? a:gcd(b,a%b);
}
int lcm(int a,int b) {
	return a/gcd(a,b)*b;//哇这里可能溢出了! 
}
int main() {
	int T,n,a,b;
	cin>>T;
	while(T--) {
		cin>>n>>a;//考虑了n==1的情况,这样的话下面的循环不会执行,直接输出a。但是,n总不能==0吧 
		for(int i=0; i<n-1; i++) {
			cin>>b;
			a=lcm(a,b);
		}
		cout<<a<<endl;
	}
	return 0;
}

素数与分解质因数

case1:素数判定,先判断<=1的都不是,然后利用 < sqrt + 1 这个范围来%遍历,切忌不是<=,因为不能包括该数本身【素数对应的叫合数】

#include <iostream>
#include<cmath>
using namespace std;
int main() {
	int n;
	while(cin>>n) {
		int k=(int)sqrt(n)+1,ok=1;
		if(n<=1) ok=0;
		for(int i=2; i<k; i++)
			if(n%i==0) {
				ok=0;
				break;
			}
		if(ok) cout<<"yes\n";
		else cout<<"no\n";
	}
	return 0;
}

case2:素数筛选。埃筛,每次将素数的倍数都标记为合数,而每个没标记到的都是素数。有个小技巧就是从i*i开始往后标记降低复杂度,O(nloglogn)

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int prime[10005];//保存筛到的素数
int pri_n=0;//素数个数
int mark[10005];//1:已标记 不用再判断其是否为素数
void search_prime() {//埃筛,O(nloglogn)
	memset(mark,0,sizeof(mark));
	for(int i=2; i<=10000; i++) {//最多只能46000这个数量级,因为46*46=2116快到了int最大范围
		if(mark[i]) continue;//已标记		
		prime[pri_n++]=i;//没标记到的,一定是素数 
		for(int k=i*i; k<=10000; k+=i)//素数的倍数都标记上已访问(他们是非素数
			mark[k]=1;		
	}
//	for(int i=0; i<pri_n; i++)
//		cout<<prime[i]<<' ';
}
int main() {
	search_prime();
	int n, i, ok;//2<=n<=10000
	while(cin>>n) {
		for(i=0,ok=0; n>prime[i]; i++) {
			if(prime[i]%10==1) {
				printf("%d ", prime[i]);
				ok=1;
			}
		}
		if (ok) printf("\n");
		else printf("-1\n");
	}
	return 0;
}

case3:一万个素数,我就在上面基础上。。手改了遍历的范围然后刚好卡到10000个。内存32M最大开不到8e6,应该有14w左右hhh,不过这个工作应该让程序做,到10000就stop

#include<iostream>
#include<cstring>
#include<cmath>
typedef unsigned long long LL;
using namespace std;
LL prime[10005];//保存筛到的素数
int pri_n=0;//素数个数
int mark[100005];//1:已标记 不用再判断其是否为素数
void search_prime() {//埃筛,O(nloglogn)
	memset(mark,0,sizeof(mark));
	for(LL i=2; i<=104740; i++) {//最多只能46000这个数量级,因为46*46=2116快到了int最大范围
		if(mark[i]) continue;//已标记
		prime[pri_n++]=i;
		for(LL k=i*i; k<=100000; k+=i)//素数的倍数都标记上已访问(他们是非素数
			mark[k]=1;		 
	}
//	cout<<pri_n;
//	for(int i=0; i<pri_n; i++)
//		cout<<prime[i]<<' ';
}
int main() {
	search_prime();
	int n;//2<=n<=10000
	while(cin>>n) {
		printf("%d\n", prime[n-1]);
	}
	return 0;
}

case4:验证每一个>=4的偶数(even number)是两素数之和,就是用埃筛(几乎是O(n),很快!)找出题目中的数据范围内的素数,也就是2^15=32768个,我用了两个数组表示:

  • 一个顺序存储,用来遍历<=n/2的所有素数
  • 一个利用hash存储,O(1)判断 n-prime[i] 是否也是素数。由此,程序的复杂度已几乎最低了,空间开销不算啥
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int prime[35000];//32768
int pri_map[35000];//O(1)判断是否是素数
int mark[35000];
void search_pri() {
	memset(pri_map,0,sizeof(pri_map));
	memset(mark,0,sizeof(mark));
	int pri_n=0;
	for(int i=2; i<32768; i++) {
		if(mark[i]) continue;
		prime[pri_n++]=i;
		pri_map[i]=1;
		for(int j=i*i; j<32768; j+=i)
			mark[j]=1;
	}
//	cout<<pri_n<<'\n';
//	for(int i=0; i<pri_n; i++) cout<<prime[i]<<' ';
//	for(int i=0;i<1000;i++) cout<<pri_map[i]<<" ";
}
int main() {//15:27-15:54
	search_pri();
	int n;
	while(cin>>n, n!=0) {
		int cnt=0;
		for(int i=0; prime[i]<=n/2; i++) {
			if(pri_map[n-prime[i]]) cnt++;
		}
		printf("%d\n", cnt);
	}
	return 0;
}

case5:质因数的个数,王道给的方法就是遇到能整除的质数,n就不断除,直到%结果!=0为止(同时cnt++);技巧就是只需要存储到sqrt(n)的规模,因为只可能有一个因数>sqrt(n),只要在最后判断n是否==1,若!=1说明有一个大的因数,cnt++即可

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
#define maxn 32623//int(sqrt(1e9))+1
int prime[maxn];
int mark[maxn];//从1开始 
int pri_n=0;
//虽然数据范围是10^9,但是顶多就一个 > sqrt(10^9)的素数 是他是质因数,所以留到最后判断即可
void search_pri() {
	memset(mark,0,sizeof(mark));
	for(int i=2; i<maxn; i++) {
		if(mark[i]) continue;
		prime[pri_n++]=i;
		if(i>int(sqrt(maxn))) continue;//简化运算		
		for(int j=i*i; j<maxn; j+=i)
			mark[j]=1;
	}
}
int main() {//15:27-15:54
	search_pri();
	int n;
	while(scanf("%d", &n) != EOF) {
		int cnt=0;
		for(int i=0; n>=prime[i]&&i<pri_n; i++) {
			while(n%prime[i]==0) { //以while代替if
				n/=prime[i];
				cnt++;
			}
		}
		if(n!=1) cnt++;//说明还有一个>32623的素数 
		cout<<cnt<<'\n';
	}
	return 0;
}

有个更巧妙的方法就是,不需要求出all prime,因为如果有合数被整除,它的合数因子之前一定被小质数分解了,so

#include<iostream>
#include<cmath>
int main(){
    int x,i;
    while(scanf("%ld",&x)!=EOF){
        int cnt=0;
        int a=(int)sqrt(x)+1;
        for(i=2;i<a;i++)
            while(x%i==0){
                cnt++;
                x/=i;
            }
        printf("%d\n",x>1?cnt+1:cnt);
    }
}

case6:因数的个数,1e9规模,O(n)必TLE。想到,如果发现了一个因数,说明除以该因数的值也是一个因数,so每次找到一个cnt就+2,而且只要遍历到int(sqrt(n))就可以了,此外有个特殊情况就是eg 4=2*2 恰好是平方的情况,这也是遍历到int(sqrt(n))的原因(sqrt是向下取整),if 是平方的情况cnt++即可。牛客OJ能AC

#include<iostream>
#include<cmath>
using namespace std;
int main() {
	int n, a;
	while(cin>>n, n!=0) {
		while(n--) {
			cin>>a;
			if(a==1) printf("1\n");
			else {
				int i, cnt=2;//1和自己一定是
				for(i=2; i*i<a; i++) { //"等于"在后面单独判断 
					if(a%i==0) cnt+=2;
				}
				if(i*i==a) cnt++;
				printf("%d\n", cnt);
			}
		}
	}
	return 0;
}

但是这个方法在codeup还是TLE【改scanf 就过了!】。有数学方法:对n分解质因数。eg:360=2^3+3^2+5,

so,360的约数只能为 2^a*3^b*5^c2,而 a的范围(0-3),b的范围(0-2),c的范围(0-1),因此360的约数有4*3*2=24个。注意:

  • 最后要判断是不是除到1了!不是的话说明后面还有一个大素数没分解(且其次数一定是1)ans要*2
  • 这里新学了一个在for循环里,i<pri_n&&sqrt(x)>=prime[i],因为如果输入数字的根号都小于prime了话,肯定不能再除了;我之前用的是x>=prime[i](其中x是在不断被除,而变小的)也OK,但是由OJ表明,用开始的sqrt 更快,why?
  • 要用scanf,不然怎么都超时
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
//#define maxn 40000
const int maxn=35000;
int prime[maxn];
int mark[maxn];
int pri_n=0;

int main() {
	memset(mark, 0, sizeof(mark));
	for(int i=2; i<maxn; i++) {
		if(mark[i]) continue;
		prime[pri_n++]=i;
		if(i>sqrt(maxn)) continue;//不然下面可能也超int范围
		for(int j=i*i; j<maxn; j+=i)
			mark[j]=1;
	}
	int n, a;
	while(scanf("%d", &n)!=EOF, n!=0) {
		while(n--) {
			int cnt=0, ans=1;
			scanf("%d", &a);
			int k=sqrt(a), ok=0;
			for(int i=0; i<pri_n&&k>=prime[i]; i++) {//这个k>=prime[i]很传神啊
				while(a%prime[i]==0) {
					ok=1;
					cnt++;
					a/=prime[i];
				}
				if(ok) {
					ans*=cnt+1;
					cnt=ok=0;
				}
			}
			if(a!=1) ans*=2;//还有一个大的素数在后面,再乘(1+1)
			printf("%d\n", ans);
		}
	}
	return 0;
}

二分求幂

case1:快速幂,原理就是把幂转换为2进制,位数为1就要乘一下(0相当于幂是2^0=1不用算),同时a作为每次指数上升的权重(也就是对应了10进制转2进制时候的,低位到高位不断升权),另有两提高效率技巧:

  • 用b&1 代替 b%2(&1是只和最后一位比)
  • b>>=1 代替 b/=2
#include<iostream>
using namespace std;
int main() {//21:02->21:18
	int a,b;
	while(scanf("%d%d", &a,&b)!=EOF) {
		if(!a&&!b) break;
		int ans=1;
		while(b>0) {
			if(b&1!=0)//运算更快 
				ans=ans*a%1000;
			b>>=1;//右移1bit就是/2 
			a=a*a%1000;
		}
		printf("%d\n", ans);
	}
	return 0;
}

case2:求等差与等比数列,主要就是等比数列需要用快速幂,还有就是取模的问题(我不知道为啥用加法乘法取模公式后,反而错了。。?)

#include<iostream>
using namespace std;
typedef unsigned long long LL;
const int M=200907; 
int main() {//21:27->21:50+
	int n,k;
	scanf("%d", &n);
	LL a,b,c;
	while(n--) {
		scanf("%lld%lld%lld%d", &a,&b,&c,&k);
		if(c-b==b-a) {//等差
			LL d=b-a;
//			printf("%lld\n", (c%M+((k-3)%M*(d%M))%M)%M);//说了abc都是正数,而且是非递减的,so d也>0 
			printf("%lld\n", (c+(k-3)*(d))%M);	
		} 
		else { //等比
			LL q=b/a, ans=1;//不会除以0,放心除
			k--;//因为是a*q^(k-1)
			while(k>0) {
				if(k&1!=0)//运算更快 
					ans=(ans%M)*(q%M)%M;
				k>>=1;//右移1bit就是/2 
				q=(q%M)*(q%M)%M;
			}
			printf("%lld\n", (ans%M)*(a%M)%M);
		}
	}
	return 0;
}

case3:矩阵快速幂,完全就是把快速幂原来的int相乘变成矩阵相乘(矩阵幂一定是方阵),而矩阵乘法则是需要用三重循环,前两重用于控制行和列,第三用于对元素乘积进行求和,而pow操作和之前近乎完全一样,只是调用矩阵乘法的函数即可,

  • ans(这里是b)的初始化问题,要用单位矩阵(主对角线全1)!并且刚开始b也要menset为0!(我WA的原因)
  • 结构体其实就是一个二维数组,但是这样就可以直接 return 结构体,这样IO明确的函数,可以使程序更清晰吧
  • 矩阵乘法中 需要定义一个临时的menset为0的matirx(即引入第三个矩阵)来保存乘法结果,然后return 它作为乘法ans

(优先结构体的方法,因为二维数组传参很麻烦)

#include<iostream>
#include<cstring>
using namespace std;
const int M=9973;
const int maxn=11;
int T,n,k;
struct Mat {
	int mat[maxn][maxn];
} a,b;
Mat mul(Mat a, Mat b) {
	Mat t;
	memset(t.mat,0,sizeof(t.mat));
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			for(int k=1; k<=n; k++)
				t.mat[i][j]=(t.mat[i][j]+a.mat[i][k]%M*(b.mat[k][j]%M))%M;//i,j控制行列,k控制每一项的乘积求和
	return t;
}
Mat Pow(Mat a, Mat b) {
	while(k>0) {
		if(k&1)
			b=mul(a,b);
		a=mul(a,a);
		k>>=1;
	}
	return b;
}
int main() {
	cin>>T;
	while(T--) {
		memset(a.mat,0,sizeof(a.mat));
		memset(b.mat,0,sizeof(b.mat));//这个也要先初始化! 
		scanf("%d%d", &n,&k);//k>=2
		for(int i=1; i<=n; i++)//涉及矩阵快速幂,一定是方阵
			for(int j=1; j<=n; j++) 
				scanf("%d", &a.mat[i][j]);			
		for(int i=1; i<=n; i++) b.mat[i][i]=1; //存ans,初始化为1。卧槽为什么放前面b会被men成0啊
		b=Pow(a,b);
//		for(int i=1; i<=n; i++) {
//			for(int j=1; j<=n; j++)
//				printf("%d ", b.mat[i][j]);
//			cout<<endl;
//		}
		int sum=0;
		for(int i=1; i<=n; i++)
			sum+=b.mat[i][i]%M;
		printf("%d\n", sum%M);
	}
	return 0;
}
/*
5
3 2
1 1 1
1 1 1
1 1 1
*/

还可以用二维数组传参,有些麻烦,虽然可以传数组地址,从而修改全局,但是毕竟不方便return, 不好控制

#include<iostream>
#include<cstring>
using namespace std;
const int M=9973;
const int maxn=11;
int a[maxn][maxn];
int b[maxn][maxn];
int T,n,k;
void mul(int a[][maxn], int b[][maxn]) { //这里传的是地址,可以改变数据实际的
	int t[maxn][maxn];
	memset(t,0,sizeof(t));
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			for(int k=1; k<=n; k++)
				t[i][j]=(t[i][j]+a[i][k]%M*(b[k][j]%M))%M;//i,j控制行列,k控制每一项的乘积求和
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++) {
			b[i][j]=t[i][j];
		}
}
void Pow(int a[][maxn], int b[][maxn]) {
	while(k>0) {
		if(k&1!=0) {
			mul(a,b);//传的是地址,可以改变实际的a,b数组的值!
		}
		mul(a,a);
		k>>=1;//除以2
	}
}
int main() {
	cin>>T;
	while(T--) {
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));//这个也要先初始化!
		scanf("%d%d", &n,&k);//k>=2
		for(int i=1; i<=n; i++)//涉及矩阵快速幂,一定是方阵
			for(int j=1; j<=n; j++)
				scanf("%d", &a[i][j]);
		for(int i=1; i<=n; i++) b[i][i]=1; //存ans,初始化为1。卧槽为什么放前面b会被men成0啊
		Pow(a,b);
//		for(int i=1; i<=n; i++) {
//			for(int j=1; j<=n; j++)
//				printf("%d ", b[i][j]);
//			cout<<endl;
//		}
		int sum=0;
		for(int i=1; i<=n; i++)
			sum+=b[i][i]%M;
		printf("%d\n", sum%M);
	}
	return 0;
}

高精度(大数加减乘)

加法(牛客)codeup),用的聪神之前讲的模板,步骤为:正序读入->倒序存储->从低位相加->倒序输出。下面是假设两数都>=0的情况,牛客AC,codeup WA50%,注意:

  • 先比较谁大,大的存到a[]里面,然后把s1.length()的遍历一遍就行,ans存在a[]里面
  • 最后要检测a[k1+1]是否有进位
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1001;
int a[maxn],b[maxn];
string s1,s2;
int main() {
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	while(cin>>s1>>s2) {
		if(s1.length()<s2.length() || (s1.length()==s2.length()&&s1<s2)) {
			string t=s1;
			s1=s2;
			s2=t;
		}
		int k1=s1.length();
		int k2=s2.length();
		for(int i=1; i<=k1; i++)//倒序存储
			a[i]=s1[k1-i]-'0';
		for(int i=1; i<=k2; i++)
			b[i]=s2[k2-i]-'0';

		for(int i=1; i<=k1; i++) {//从低位开始加
			a[i]+=b[i];
			if(a[i]>=10) {
				a[i+1]++;//进位
				a[i]-=10;
			}
		}
		if(a[k1+1]!=0) k1++;//检查最后是否有进位 
		for(int i=k1; i>=1; i--)
			printf("%d", a[i]);
		printf("\n");
	}
	return 0;
}

减法。这里都假设没有负号,如果有 看是否需要转化加法即可,与加法不同的除了判断正负、“借位”操作,还有需要从第一个非0数字开始输出,同时如果ans就是0,也需要输出(这里我使用的方法是,先找到第一个非0的位置(到2!因为ans可能就是0,最后一位无论如何都要输出),然后从这开始输出)

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1001;
int a[maxn],b[maxn];
string s1,s2;
int main() {
	while(cin>>s1>>s2) { //假设都是正的,其实就算有负的,判断符号位之后,再看是否需要转为加法运算
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		string c="";//char类型不能定义为空,只好用了string
		if(s1.length()<s2.length() || s1.length()==s2.length()&&s1<s2) {
			c='-';
			string t=s1;
			s1=s2;
			s2=t;
		}
		int k1=s1.length();
		int k2=s2.length();
		int i;
		for(i=1; i<=k1; i++) a[i]=s1[k1-i]-'0';
		for(i=1; i<=k2; i++) b[i]=s2[k2-i]-'0';
		for(i=1; i<=k1; i++) {
			a[i]-=b[i];
			if(a[i]<0) {
				a[i+1]--;
				a[i]+=10;
			}
		}
		printf("%s", c.c_str());
		for(i=k1; i>=2; i--)
			if(a[i]!=0) break;//如果最后是0,则要输出0
		for(int j=i; j>=1; j--)
			printf("%d", a[j]);
		printf("\n");
	}
	return 0;
}

乘法,需要用c来存储ans,与之前不同之处就在于,每次是c[i+j-1]+=a[i]*b[j] 若>=10,则该位保留个位数(%10),而其上一位加上“十位数”的值【本质是我们小学学的列竖式的乘法算法】

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1005;
int a[maxn],b[maxn],c[maxn*maxn];//c来存储结果
string s1,s2;

int main() {
	while(cin>>s1>>s2) {
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		memset(c,0,sizeof(c));
		int k1=s1.length();
		int k2=s2.length();
		int i,j;
		for(i=1; i<=k1; i++) a[i]=s1[k1-i]-'0';
		for(i=1; i<=k2; i++) b[i]=s2[k2-i]-'0';

		for(i=1; i<=k1; i++) {
			for(j=1; j<=k2; j++) {
				c[i+j-1]+=a[i]*b[j];
				if(c[i+j-1]>=10) {
					c[i+j]+=c[i+j-1]/10;//进位 
					c[i+j-1]%=10;//取个位
				}
			}
		}
		for(i=k1+k2; i>=2; i--)//记录第一个非0值的位置 ,一定是到2,因为最后一个无论如何都要输出,因为可能ans就是0 
			if(c[i]!=0) break;
		for(j=i; j>=1; j--)
			printf("%d", c[j]);
		printf("\n");
	}
	return 0;
}

case1:N的阶乘,???????

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值