🌸进制与整除训练
🍁Strange_Donation
地产大亨Q先生临终的遗愿是:拿出100万元给X社区的居民抽奖,以稍慰藉心中愧疚。
麻烦的是,他有个很奇怪的要求:
100万元必须被正好分成若干份(不能剩余)。 每份必须是7的若干元。 比如:1元, 7元,49元,343元,…
相同金额的份数不能超过5份。
在满足上述要求的情况下,分成的份数越多越好!
请你帮忙计算一下,最多可以分为多少份?
分析
换个角度考虑,如果拿出1234567890元分给居民,每份必须是10的若干次方元,
且相同金额份数不超过9份,那么最多可以分为多少份? 那么只有1种情况,10元9份,100元8份,1000元7份,10000元6份…. 可以看到,这实际上是进制的问题,即题目要求的是以7进制形式表示后的数字,然后把每位数字相加即可求出份数。题意。
1 | public class Strange_Donation { |
🍁Weight_weighing
用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。如果只有5个砝码,重量分别是1,3,9,27,81则它们可以组合称出1到121之间任意整数重量(砝码允许放在左右两个盘中)。
本题目要求编程实现:对用户给定的重量,给出砝码组合方案。
例如:
1 | 用户输入: |
要求程序输出的组合总是大数在前小数在后。
可以假设用户的输入的数字符合范围1~121。
分析
暴力解法
1 | public class Weight_weighing { |
🍁解法二:利用进制
1 | public class Weight_weighing { |
🍁Nim_Pile
有3堆硬币,分别是3,4,5
二人轮流取硬币。
每人每次只能从某一堆上取任意数量。
不能弃权。
取到最后一枚硬币的为赢家。
求先取硬币一方有无必胜的招法。
分析
这个题有固定的解法,用二进制模2的加法/异或。
具体意思是:将所有堆的数目进行模2加法/异或,如果加起来全为0,那么将要抓堆的这个人就必输了;如果不全为0,那么这个人通过计算抓堆的数量就会让对方输。
举例来说:一共4堆:2,5,12,14
二进制对应:
0010
0101
1100
1110
0101
对每个堆的数目进行异或后结果不为0,所以将要抓堆的这个人不会输,那么他如何让对方输呢?
就是在他抓取一次后给对方留的堆的数目异或起来为0,由0101->0000,将左起第2位和第4位变0,分析造成位结果为1的原因,把某一堆堆数量的对应位取反就可以了(0->1or1->0)
由异或运算性质,0异或任何数,其结果=任何数,1异或任何数,其结果=把该数取反。
这个异或运算的结果恰好就是我们想要达到的效果。所以我们可以将所有数异或运算后的结果再与某堆数目进行异或,得到的结果就是该堆剩下的数目。
1 | public class Nim_Pile { |
🍁The_Least_Multiple_of_Number
如果两个数很大,怎样求最大公约数,最小公倍数?
如果是n个数呢?比如1000个数的最小公倍数
分析
求解n个数的最小公倍数一般有两种做法:
1.分解质因数法:先把这几个数分解质因数,再把它们一切公有的质因数和其中几个数公有的质因数以及每个数的独有的质因数全部连乘起来,所得的积就是它们的最小公倍数.
12 15 8 9
12=223
15=3*5
8=222
9=3*3
其中12和8公有两个2,而12,15,9则公有质因子3,剩下的15独自有一个5,8独自有一个2,9独自有一个3
因此:最小公倍数为22352*3=360
这种方法个人觉得适合人去算,并不适合计算机去计算。计算机更适合采用第二种方法。
2.公式法两两运用
假设现在要求最小公倍数的两个数为x,y,他们的最大公约数为p,最小公倍数为q。则xy=pq,也就是说只要求得两个数的最大公约数就可求得这两个数的最小公倍数。
但是题目中要求的是n个数的 最小公倍数,这里只需要用最小公倍数代替原来的两个数即可。
例如:12 15 8 9
第一步:求得12和15的最小公倍数为60
第二部:求得60和8的最小公倍数为120
第三步:求得120和9的最小公倍数为360
所以,原问题转换为求两个数的最大公约数。最大公约数有几种求法,第一种对于小整数的辗转相除,大整数用辗转相减,还有优化版本的辗转相减法,这里给定数字范围为int因此直接采用辗转相除法。
辗转相除求最大公约数O(lgn)
递归写法:
1 | int gcd(int a,int b){ |
非递归写法:
1 | int gcd(int a,int b){ |
利用xy=qp得到最小公倍数q=x*y/p;
1 | public class The_Least_Multiple_of_Number { |
🍁One_step_away
从昏迷中醒来,小明发现自己被关在X星球的废矿车里。矿车停在平直的废弃的轨道上。他的面前是两个按钮,分别写着“F”和“B”。 小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。按F,会前进97米。按B会后退127米。透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。或许,通过多次操作F和B可以办到。 矿车上的动力已经不太足,黄色的警示灯在默默闪烁…每次进行 F 或 B 操作都会消耗一定的能量。小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。 请填写为了达成目标,最少需要操作的次数。
暴力解法
1 | public class One_step_away { |
解法二:坐标
将轨道视为一条坐标轴,探头视为原点,由车指向探头方向为正方向,则车的初始坐标为-1,当车的坐标<127米时,车前进一次,坐标+97,当车的坐标>=127时,车后退一次,坐标 -127,
1 | public class One_step_away { |
解法三:扩展欧几里德算法
详情可查看扩展欧几里德算法
当我们算出 97x+127y=gcd(97,127)
的第一组解(x0,y0)
后;
我们可以得到 97x+127y=1
的第一组解:
x=x0*1/gcd(97,127);
y=y0*1/gcd(97,127);
然后我们输出 abs(x)+ abs(y)即可。
1 | public class One_step_away { |
🍁Sum_of_Fractions
如果求 1/2 + 1/3 + 1/4 + 1/5 + 1/6 + …. + 1/100 = ? 要求绝对精确,不能有误差。
分析
常规思路,要求绝对精确,所以利用API
1 | public class Sum_of_Fractions { |
🍁Nth_Prime
第1个素数是2,第2个素数是3,…
求第100002(十万零二)个素数
利用API
1 | public class Nth_Prime { |
常规方法,编写一个判断素数的方法,并将素数储存
1 | public class Nth_Prime { |
1 | public class Nth_Prime { |