第一题

地产大亨Q先生临终的遗愿是:拿出100万元给X社区的居民抽奖,以稍慰藉心中愧疚。
麻烦的是,他有个很奇怪的要求:

1.100万元必须被正好分成若干份(不能剩余)。
每份必须是7的若干次方元。
比如:1元, 7元,49元,343元,…
2.相同金额的份数不能超过5份。
3.在满足上述要求的情况下,分成的份数越多越好!

请你帮忙计算一下,最多可以分为多少份?


解法:
  先上暴力法,观察发现题目中给出的情况皆是7的若干次方,每位不超过5,设置一个进制计数数组picks做7进制记录,直接枚举出100万以内的所有可能的数

public class Main {public static void main(String[] args) {int[] sevens = {1, 7, 49, 343, 2401, 16807, 117649, 823543};int[] picks  = new int[sevens.length];Arrays.fill(picks, 0);pick(sevens, picks, 0);}/*** 暴力法,组合判断* @param sevens* @param picks* @param k*/public static void pick(int[] sevens, int[] picks, int k) {if (k == picks.length) {if (judge(sevens, picks)) {System.out.println(getParts(picks));}return;}if (picks[k] == 5) {pick(sevens, picks, k + 1);} else {picks[k] += 1;pick(sevens, picks, k);picks[k] -= 1;pick(sevens, picks, k + 1);}}public static boolean judge(int[] sevens, int[] picks) {int sum = 0;for (int i = 0; i < picks.length; i++) {sum += sevens[i] * picks[i];}if (sum == 1000000) {return true;} else {return false;}}public static int getParts(int[] picks) {int parts = 0;for (int i = picks.length - 1; i >= 0; i--) {parts += picks[i];}return parts;}

  再上课堂上老师教学的简便方法,将目标1000000转换城7进制表达式,直接输出。从那天起,笔者又重新感受到了被数学支配的恐惧 (摊手)。

/*** 7进制,每位不超过5*/
public class Main {public static void main(String[] args) {char[] c = Integer.toString(1000000, 7).toCharArray();int count = 0;for (char ch : c){count += (ch-'0');}System.out.println(count);}
}

第二题

用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。
如果只有5个砝码,重量分别是1,3,9,27,81
则它们可以组合称出1到121之间任意整数重量(砝码允许放在左右两个盘中)。
本题目要求编程实现:对用户给定的重量,给出砝码组合方案。
例如:

用户输入:
5
程序输出:
9-3-1
用户输入:
19
程序输出:
27-9+1

要求程序输出的组合总是大数在前小数在后。
可以假设用户的输入的数字符合范围1~121。


解法:
  先上暴力,数字组合+,-,不拿,得算式,验证结果。

public class Main {public static void main(String[] args) {int[] fama = {1, 3, 9, 27, 81};int[] status = {0,0,0,0,0};int n = 19;f1(fama, status, 0, n)}/*** 暴力法,全组合** @param fama* @param status* @param k* @param goal*/public static void f1(int[] fama, int[] status, int k, int goal) {if (k == -1) {if (goal == 0) {boolean first = true;for (int i = status.length - 1; i >= 0; i--) {int num = fama[i] * status[i];if (num != 0) {if (num > 0 && !first) {System.out.print("+");}System.out.print(num);first = false;}}}return;}status[k] = 1;f1(fama, status, k - 1, goal - fama[k]);status[k] = -1;f1(fama, status, k - 1, goal + fama[k]);status[k] = 0;f1(fama, status, k - 1, goal);}
}

  非递归方法初稿,代码可能看起来比较多,但是思路清晰

public class Main {public static void main(String[] args) {int[] fama = {1, 3, 9, 27, 81};int n = 19;System.out.println(f2(fama, n));}/*** 将n化为3进制数,并且不允许当n = 2,当发生时继续取余得到-1** @param fama* @param n*/public static String f2(int[] fama, int n) {StringBuilder result = new StringBuilder();/*求每个砝码状态*/while (n != 0) {int remainder = n % 3;if (remainder < 2) {/*余数为0,1的情况*/n /= 3;} else {/*余数为2的情况*/n = n / 3 + 1;remainder -= 3;}result.insert(0, ",").insert(0, remainder);}/*补零*/int[]    status  = new int[5];String[] statusB = result.toString().split(",");for (int i = 0; i < status.length - statusB.length; i++) {result.insert(0, "0,");}/*转换,装填*/statusB = result.toString().split(",");for (int i = 0; i < status.length; i++) {status[i] = Integer.valueOf(statusB[i]);}/*根据status,拼接出算式*/StringBuilder stringBuilder = new StringBuilder();for (int i = fama.length - 1; i >= 0; i--) {switch (status[fama.length - 1 - i]) {case -1:stringBuilder.append("-").append(fama[i]);break;case 0:break;case 1:stringBuilder.append("+").append(fama[i]);break;}}stringBuilder.replace(0, 1, "");return stringBuilder.toString();}
}

  再来最后一稿,先上思路伪代码

f(int[] famas, int n)
首先找到>=n的那个砝码k
如果X(k)=n
  返回 n
如果X(k)>n
  subSum=[X(0)+X(1)+…+X(k)]
  如果subSum小于n //小数字全加起来都不够表示n,说明要靠大的减
    返回 X(k) + [f(n-subSum)]     //这里需要注意,下层返回的算式是不带括号的,所以如果上层是减的话,需要进行一次转换
  如果subSum大于等于n //小数字全加起来够表示n
    返回 X(k-1) + “+” + [f(n-subSum)]

reverse(String s)
将加减互换得到s1
返回 “-” + s1

public class Main {static int[] famas = {1, 3, 9, 27, 81};public static void main(String[] args) {for (int i = 1; i <= 121; i++) {System.out.println(i + ":" + f(i));}}public static String f(int n) {int a     = 1;int index = 0;while (a < n) {a *= 3;index++;}/*a 只会 >= n*/if (a == n) return String.valueOf(n);else {//a>nint subSum = 0;for (int i = 0; i < index; i++) {subSum += famas[i];}if (subSum >= n) {int thinnerOne = famas[index - 1];return thinnerOne + "+" + f(n - thinnerOne);} else {return a + reverse(f(a - n));}}}public static String reverse(String s) {s = s.replace("+", "#").replace("-", "+").replace("#", "-");return "-" + s;}
}

第三题

有3堆硬币,分别是3,4,5
二人轮流取硬币。
每人每次只能从某一堆上取任意数量。
不能弃权。
取到最后一枚硬币的为赢家。
求先取硬币一方有无必胜的招法。


解法:
  尼姆堆问题关键在于求当前状态的Nim-sum,如果在自己手上的Nim-sum为0,则一定输;如果非0,则一定可以将0的局面给对手,则一定赢。
  为什么得出这个结论呢?因为考虑到Nim-sum计算的特性(全堆异或),如果一开始给到对手的是0局面,则对手不管怎么动都会促成一个非0的局面,则你接下来反手一定可以给对方一个0的局面,到最后的情况就是0xor0xor0….即全0局面,胜。
  所以我们要创造一个必胜的局面,就需要首先己方的初始状态Nim-sum一定非0,且走出的第一步一定要促成0的局面

public class Main {public static void main(String[] args) {int   n     = 3;int[] heaps = new int[n];heaps[0] = 3;heaps[1] = 4;heaps[2] = 5;List<Integer[]> methods = new ArrayList<>();for (int i = 0; i < n; i++) {for (int j = 0; j <= heaps[i]; j++) {heaps[i] -= j;if (isNimSum0(heaps)) {//这种下手方法必胜Integer[] method = new Integer[2];method[0] = i + 1;method[1] = j;//意为从i堆中拿走j个methods.add(method);}heaps[i] += j;}}for (Integer[] method : methods) {System.out.println(Arrays.asList(method));}}public static boolean isNimSum0(int[] heaps) {int num = heaps[0];for (int i = 1; i < heaps.length; i++) {num = num ^ heaps[i];}return num == 0;}
}

第四题

如果两个数很大,怎样求最大公约数,最小公倍数?
如果是n个数呢?比如1000个数的最小公倍数


解法:
  使用BigInteger类的gcd()方法求最大公约数

public static String gcd1(int a, int b) {BigInteger bigInteger = new BigInteger(String.valueOf(a));return bigInteger.gcd(new BigInteger(String.valueOf(b))).toString();
}

  使用BigInteger类的multiply()方法求乘积,用divide()方法除以上面得出的最大公约数,得最小公倍数

第五题

从昏迷中醒来,小明发现自己被关在X星球的废矿车里。
矿车停在平直的废弃的轨道上。
他的面前是两个按钮,分别写着“F”和“B”。
小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。
按F,会前进97米。按B会后退127米。
透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。
他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。
或许,通过多次操作F和B可以办到。
矿车上的动力已经不太足,黄色的警示灯在默默闪烁…
每次进行 F 或 B 操作都会消耗一定的能量。
小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。
请填写为了达成目标,最少需要操作的次数。


解法:
  此题就是求不定方程97x+127y = 1的整数解
已知扩展欧几里德定理
Ax+By = gcd(A,B)一定有解

Bx’+(A%B)y’ = gcd(B, A%B)
由以上两式子,且根据欧几里德定理gcd(A,B) = gcd(B, A%B)
可得Ax+By = Bx’+(A%B)y’
所以
Ax+By = Bx’+(A%B)y’ = Bx’ +[A–(A/B)*B]y’ = Bx’ + Ay’ –(A/B)*By’ = Ay’+B[x’–(A/B)y’]

A [x] + B [y] = A [y’] + B {[x’–(A/B)y’]}
得出恒等关
x = y’ , y = [x’ – (A/B)y’]
即当下层求得一对x’, y’时,上层即可通过恒等式得出当前层的x, y继续返回上层
此时递归关系得出
由于gcd算法最终递归出口为b == 0
所以递归出口条件为b == 0
且当b == 0时, Ax = gcd(A,0) = A
得此时x = 1, y = 上层传入的b = 0

这道题代码倒是不怎么重要,重要的是要知道扩展欧几里德定理
从那天起,笔者又重新感受到了被数学按在地上摩擦的恐惧 (摊手)。
当然要是题目条件允许的话,可以使用暴力求解,比如从-1000,-1000试到1000,1000,不行就再大点(逃

public class Main {/*** 遇到求解不定方程,首先可以想到暴力法* 然后就是将方程化为Ax+By = gcd(A,B)*m** @param args*/public static void main(String[] args) {int[] xy = new int[2];e_gcd(97, 127, xy);System.out.println(xy[0] + " " + xy[1]);}/*** 扩展欧几里德算法求解不定方程 Ax + By = gcd(A,B)*m (m是任意整数,这里只给出1的情况)* 可以求解Ax + By = gcd(A,B),然后将得出的x,y分别乘m就好* 再扩展的Ax + By = n的一般情况笔者暂时未思考** @param a  上层向下传入的A* @param b  上层向下传入的B* @param xy 接收下层返回的x,y* @return*/private static void e_gcd(int a, int b, int[] xy) {if (b == 0) {xy[0] = 1;xy[1] = 0;return;}e_gcd(b, a % b, xy);int x = xy[0];int y = xy[1];xy[0] = y;xy[1] = x - a / b * y;}
}

第六题

如果求 1/2 + 1/3 + 1/4 + 1/5 + 1/6 + …. + 1/100 = ?
要求绝对精确,不能有误差。


解法:
  自己定义分数计算类,不要使用浮点

public class Main {public static void main(String[] args) {Fraction sum = new Fraction(new BigInteger(String.valueOf(1)), new BigInteger(String.valueOf(2)));for (int i = 3; i <= 100; i++) {sum.add(new Fraction(new BigInteger(String.valueOf(1)), new BigInteger(String.valueOf(i))));}System.out.println(sum);}
}class Fraction {private BigInteger numerator;private BigInteger denominator;public Fraction(BigInteger numerator, BigInteger denominator) {this.numerator = numerator;this.denominator = denominator;}public Fraction add(Fraction b) {this.numerator = this.numerator.multiply(b.denominator).add(this.denominator.multiply(b.numerator));this.denominator = this.denominator.multiply(b.denominator);reduction(this);return this;}public Fraction subtract(Fraction b) {this.numerator = this.numerator.multiply(b.denominator).subtract(this.denominator.multiply(b.numerator));this.denominator = this.denominator.multiply(b.denominator);reduction(this);return this;}public Fraction multiply(Fraction b) {this.numerator = this.numerator.multiply(b.numerator);this.denominator = this.denominator.multiply(b.denominator);reduction(this);return this;}public Fraction divide(Fraction b) {this.numerator = this.numerator.multiply(b.denominator);this.denominator = this.denominator.multiply(b.numerator);reduction(this);return this;}public Fraction reduction(Fraction r) {BigInteger greatestCommonDivisor = r.numerator.gcd(r.denominator);r.numerator = r.numerator.divide(greatestCommonDivisor);r.denominator = r.denominator.divide(greatestCommonDivisor);return r;}@Overridepublic String toString() {return this.numerator + "/" + this.denominator;}
}

第七题

第1个素数是2,第2个素数是3,…
求第100002(十万零二)个素数


解法:
  使用埃拉托斯特尼筛法,在这个基础上,我们还需要注意一个点——数组长度设置,这里笔者引用了素数范围的AKS证明:

This is an algorithm to test whether or not a given integer is prime.
It’s called the AKS primality test https://en.m.wikipedia.org/wiki/AKS_primality_test
And can be done in polynomial time, which is usually considered a decent amount of time.
Now if you’re trying to compute the nth prime, it has been proven that the nth prime must be greater than
  nln(n)+n(ln(ln(n))−1)
and less than
  nln(n)+nln(ln(n))
When n≥6.
So if you’re searching for the nth prime, I’d look in this gap.

public class Main {public static void main(String[] args) {long start = Calendar.getInstance().getTimeInMillis();int  n     = 100002;System.out.println(getNumberNPrime(n));long end = Calendar.getInstance().getTimeInMillis();System.out.println((end - start) + "ms");}public static int getNumberNPrime(int n) {int   size = n < 6 ? 12 : (int) (Math.ceil(n * Math.log(n) + n * Math.log(Math.log(n))));int[] nums = new int[size];Arrays.fill(nums, 0);double len = Math.sqrt(nums.length);for (int i = 2; i < len; i++) {if (nums[i] == 1) continue;for (int j = i * i; j < nums.length; j += i) {nums[j] = 1;}}int count = 0;int index = -1;for (int i = 2; i < nums.length; i++) {if (nums[i] == 0) {count++;}if (count == n) {index = i;break;}}return index;}
}

该算法经过笔者的细(随)心(意)优化后,计算100002th素数在笔者电脑上只需要要34ms,10000000th只需要3s

蓝桥杯赛前冲刺补习第四课——《数学知识的应用》相关推荐

  1. 蓝桥杯赛前冲刺补习第一课——《暴力与枚举》

    第一题 美国数学家维纳(N.Wiener)智力早熟,11岁就上了大学. 他曾在1935~1936年应邀来中国清华大学讲学. 一次,他参加某个重要会议,年轻的脸孔引人注目. 于是有人询问他的年龄,他回答 ...

  2. AcWing基础算法课Level-2 第四讲 数学知识

    AcWing基础算法课Level-2 第四讲 数学知识 您将学会以下数学名词 质数,试除法,埃式筛法,线性筛,辗转相除,算术基本定理,质因数分解,欧拉函数,快速幂,费马小定理,逆元,拓展欧几里得,一次 ...

  3. 算法基础课——第四章 数学知识(三)

    第四章 数学知识(三) 如无特殊说明,所有数均为正整数. 高斯消元 高斯消元是用来解方程的,可以在 O(n3)O(n^3)O(n3)​ 时间复杂度内求解一个 nnn 个方程 nnn 个未知数 的多元线 ...

  4. 算法基础课——第四章 数学知识(一)

    第四章 数学知识(一) 如无特殊说明,所有数均为正整数 质数 质数: 针对所有大于 111​​​ 的自然数来说,如果只包含 111​​ 和本身 这两个约数,就被称为质数,或者叫素数:否则被称为 合数. ...

  5. 第十三届蓝桥杯赛前的一点总结

    比赛最主要的实现功能,需要关注各个部分之间的逻辑! 赛前的一点总结 LED 数码管 独立按键 正常处理按键 长按键功能--规定秒数 长按键功能--不规定秒数 矩阵按键 4 * 4 矩阵按键 2 * 2 ...

  6. 蓝桥杯赛前真题 Python组 Day 4

    试题 算法训练 共线 资源限制 内存限制:256.0MB C/C++时间限制:1.0s Java时间限制:3.0s Python时间限制:5.0s 问题描述 给定2维平面上n个整点的坐标,一条直线最多 ...

  7. 算法提高课-数学知识-矩阵乘法-AcWing 1303. 斐波那契前 n 项和:矩阵乘法,快速幂,线性代数

    题目分析 来源:acwing 分析: 先利用矩阵运算的性质将通项公式变成幂次形式,然后用快速幂的方法求解第 n项. 斐波那契数列的递推公式:f1=f2=1,fn=fn−2+fn−1(n≥3)f_1 = ...

  8. 软考考前冲刺第九,十章多媒体基础知识知识产权基础知识

    图形和图像的区别: 图形又被称为矢量图,而图像被称为位图. 图形是用一系列计算机指令来描述和记录的图的信息. 图形用数学的公式来记录信息. 图像是用位图的点阵来记录信息,用离散的点来记录整幅图. 图像 ...

  9. 第四章 数学知识【完结】

    本文的所有内容模板都来自于y总 目录 试除法判定质数 试除法分解质因数 朴素筛法求素数 埃氏筛法求素数 线性筛法 试除法求所有约数 约数个数和约数之和 欧几里得算法 求欧拉函数 筛法求欧拉函数 [不熟 ...

最新文章

  1. Linux 操作系统原理 — 内存 — 页式管理、段式管理与段页式管理
  2. 006_logback体系结构
  3. qt double 相减不为0_Qt音视频开发25-ffmpeg音量设置
  4. 2018/Province_Java_C/1/哪天返回
  5. Etsy如何及为什么迁移到API优先的架构
  6. 桂林电子科技大学C语言大作业,桂林电子科技大学c语言程序设计习题集及答案qvzaewzm.doc...
  7. openocd目录_OpenOCD的调试
  8. NLP 语料库 大全
  9. 怎么在html中加水印,如何在h5网页添加水印
  10. 班迪录屏- Bandicam v4.5.6
  11. Spark性能调优-Shuffle相关参数配置
  12. win7系统域服务器,win7安装域服务器
  13. 用户密码的存储与密码传输
  14. (授人以鱼不如授人以渔)mysql-connector-java各种版本下载地址
  15. Java中的@Test注解
  16. 2020计算机保研经历
  17. Frank Luna DirectX12阅读笔记:绘制进阶(第八章-第十四章)
  18. 网线专业测试软件,网线测试仪怎么用_网线测试仪的使用方法图解
  19. C++ 类的静态成员及静态成员函数
  20. 最新最安全代理服务搭建(Websocket-Web-TLS)

热门文章

  1. Win10版本号命令行查询
  2. YOLO V3基于Tensorflow 2.0的完整实现
  3. 浏览器又要进入战国时代了?
  4. 计算机屏幕坐标系转换,计算机图形窗口到视口坐标转换
  5. 2017~2018学年《信息安全》考试试题(A2卷)
  6. WPF/Silverlight深度解决方案:(七)HLSL自定义渲染特效之完美攻略(中)
  7. EditText插入QQ表情源码
  8. 作业7-1 近似求PI
  9. Android-开发技术周报,你掌握了多少
  10. “AMD Software提示和驱动程序版本不匹配?” ——Win10自动更新降级覆盖AMD驱动的解决方法