《算法笔记》学习笔记(13):数学问题

1019 数字黑洞 (20 分)
给定任一个各位数字不完全相同的 4 位正整数,如果我们先把 4 个数字按非递增排序,再按非递减排序,然后用第 1 个数字减第 2 个数字,将得到一个新的数字。一直重复这样做,我们很快会停在有“数字黑洞”之称的 6174,这个神奇的数字也叫 Kaprekar 常数。

例如,我们从6767开始,将得到

7766 - 6677 = 1089
9810 - 0189 = 9621
9621 - 1269 = 8352
8532 - 2358 = 6174
7641 - 1467 = 6174
… …
现给定任意 4 位正整数,请编写程序演示到达黑洞的过程。

输入格式:
输入给出一个 (0,10
​4
​​ ) 区间内的正整数 N。

输出格式:
如果 N 的 4 位数字全相等,则在一行内输出 N - N = 0000;否则将计算的每一步在一行内输出,直到 6174 作为差出现,输出格式见样例。注意每个数字按 4 位数格式输出。

输入样例 1:
6767
输出样例 1:
7766 - 6677 = 1089
9810 - 0189 = 9621
9621 - 1269 = 8352
8532 - 2358 = 6174
输入样例 2:
2222
输出样例 2:
2222 - 2222 = 0000

非常简单的一道题

#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<algorithm>
using namespace std;
bool cmp(int a, int b)
{
	return a > b;
}
void to_array(int n, int num[])
{
	for (int i = 0; i < 4; i++)
	{
		num[i] = n % 10;
		n /= 10;
	}
}
int to_number(int num[])
{
	int sum = 0;
	for (int i = 0; i < 4; i++)
	{
		sum = sum * 10 + num[i];
	}
	return sum;
}

int main()
{
	int n, MIN, MAX;
	scanf("%d", &n);
	int num[5];
	while (1)
	{
		to_array(n, num);
		sort(num, num + 4);
		MIN = to_number(num);
		sort(num, num + 4, cmp);
		MAX = to_number(num);
		n = MAX - MIN;
		printf("%04d - %04d = %04d\n", MAX, MIN, n);
		if (n == 0 || n == 6174)break;
	}
	return 0;
}

最小公倍数和最大公约数
最大公约数(Greatest Common Divisor)可用欧几里得算法,及辗转相除法求得

//最大公约数
int gcd(int a, int b)
{
	if (b == 0)return a;
	else return gcd(b, a % b);
}

最小公倍数(Lowest Common Multiple)可在先求出最大公约数的情况下,将两数相乘再除以最大公约数求得

int lcm(int a, int b)
{
	return a * b / gcd(a, b);
}

分数的化简及其四则运算
这部分也很简单,将分数视作一个结构体,成员包括分子和分母

//分数定义
struct Fraction
{
	int up, down;
};

化简时的三项规定
1.若分母为负数,则上下同取相反数
2.分子为0,则分母为1
3.约分:求出分子绝对值和分母绝对值的最大公约数d,然后同除以d

//分数化简
Fraction reduction(Fraction result)
{
	if (result.down < 0)
	{
		result.up = -result.up;
		result.down = -result.down;
	}
	if (result.up == 0)
	{
		result.down = 1;
	}
	else
	{
		int d = gcd(abs(result.up), abs(result.down));
		result.up /= d;
		result.down /= d;
	}
	return result;
}

四则运算就是套公式

//分数加法
Fraction add(Fraction f1, Fraction f2)
{
	Fraction result;
	result.up = f1.up * f2.down + f2.up * f1.down;
	result.down = f1.down * f2.down;
	return reduction(result);
}

//分数减法
Fraction minu(Fraction f1, Fraction f2)
{
	Fraction result;
	result.up = f1.up * f2.down - f2.up * f1.down;
	result.down = f1.down * f2.down;
	return reduction(result);
}

//分数乘法
Fraction multi(Fraction f1, Fraction f2)
{
	Fraction result;
	result.up = f1.up * f2.up;
	result.down = f1.down * f2.down;
	return reduction(result);
}

//分数除法
Fraction divide(Fraction f1, Fraction f2)
{
	Fraction result;
	result.up = f1.up * f2.down;
	result.down = f1.down * f2.up;
	return reduction(result);
}

分数输出
1.输出前先化简
2.若分母为1,则只输出分子
3.假分数以带分数方式输出,整数部分为r.up/r.down,分子部分为abs(r.up)%r.down
4.真分数按原样输出

//分数输出
void showResult(Fraction r)
{
	r = reduction(r);
	if (r.down == 1)printf("%d", r.up);
	else if (abs(r.up) > r.down)
	{
		printf("%d %d/%d", r.up / r.down, abs(r.up) % r.down, r.down);
	}
	else
	{
		printf("%d/%d", r.up, r.down);
	}
}

素数
素数的判断

bool isPrime(int n)
{
	if (n <= 1)return false;
	int sqr = (int)sqrt(1.0 * n);
	for (int i = 2; i <= sqr; i++)
	{
		if (n % i == 0)return false;
	}
	return true;
}

素数表的获取
可以使用埃式筛法,即Eratosthenes筛法。算法从小到大枚举所有数,对每一个素数,筛去他的所有倍数,剩下的就是素数。

以下是求解100以内素数的程序

const int maxn = 101;
int prime[maxn], pNum = 0;//素数数组和素数个数
bool p[maxn] = { 0 };//记录是否为素数,true表示是素数
void Find_Prime()
{
	for (int i = 2; i < maxn; i++)
	{
		if (p[i] == false)//若i是素数
		{
			prime[pNum++] = i;//把素数i放入prime数组中
			for (int j = i + i; j < maxn; j += i)
			{
				p[j] = true;//筛去i的所有倍数
			}
		}
	}
}

运行结果

在这里插入图片描述
质因子分解
先建一个结构体数组,用于存放质因子

struct factor
{
	int x, cnt;//x表示质因子,cnt表示个数
}fac[10];

判断过程:
1.枚举1~sqrt(n)范围内的所有质因子p,判断p是否是n的因子
如果p是n的因子,那么给fac数组增加质因子p,并初始化其个数为0,只给将n不断除以p,若能整除则cnt++,直到不能整除
如果不是则直接跳过
2.如果在上面步骤后n仍然大于1,说明n尤其仅有一个大于sqrt(n)的质因子么有可能是n本身,这时将这个质因子加入数组,并令其个数为1

bool isPrime(int n)
{
	if (n <= 1)return false;
	int sqr = (int)sqrt(1.0 * n);
	for (int i = 2; i <= sqr; i++)
	{
		if (n % i == 0)return false;
	}
	return true;
}
const int maxn = 101;
int prime[maxn], pNum = 0;//素数数组和素数个数
bool p[maxn] = { 0 };//记录是否为素数,true表示是素数
void Find_Prime()
{
	for (int i = 2; i < maxn; i++)
	{
		if (p[i] == false)//若i是素数
		{
			prime[pNum++] = i;//把素数i放入prime数组中
			for (int j = i + i; j < maxn; j += i)
			{
				p[j] = true;//筛去i的所有倍数
			}
		}
	}
}
struct factor
{
	int x, cnt;//x表示质因子,cnt表示个数
}fac[10];

int main()
{
	int n,num=0;//num表示不同质因子的个数
	scanf("%d", &n);
	Find_Prime();
	if (n == 1)printf("1=1\n");//若n为1则直接输出
	else
	{
		int sqr = (int)sqrt(1.0 * n);
		for (int i = 0; i < pNum && prime[i] <= sqr; i++)
		{
			if (n % prime[i] == 0)//若prime[i]是n的因子
			{
				fac[num].x = prime[i];//记录该因子
				fac[num].cnt = 0;
				while (n % prime[i] == 0)
				{
					fac[num].cnt++;
					n /= prime[i];
				}//计算该因子的次数
				num++;//不同质因子的个数+1
			}
			if (n == 1)break;
		}
		if (n != 1)//若无法被sqrt(n)以内的质因子除尽
		{
			fac[num].x = n;
			fac[num++].cnt = 1;
		}
		for (int i = 0; i < num; i++)//输出
		{
			if (i > 0)printf("*");
			printf("%d", fac[i].x);
			if (fac[i].cnt > 1)
			{
				printf("^%d", fac[i].cnt);
			}
		}
	}
}

结果
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值