蓝桥杯 历届试题 矩阵翻硬币(大数开方)
来源:互联网 发布:淘宝开店证件照要求 编辑:程序博客网 时间:2024/05/21 19:39
历届试题 矩阵翻硬币
随后,小明对每一个硬币分别进行一次 Q 操作。
对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转。
其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。
当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。
小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。
聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。
对于20%的数据,n、m <= 10^7;
对于40%的数据,n、m <= 10^15;
对于10%的数据,n、m <= 10^1000(10的1000次方)。
Q操作的实现:
import java.util.Arrays;public class Main{ // 假设0表示反面 // 小明先把硬币摆成了一个 n 行 m 列的矩阵。 static int[][] qArray = {{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}}; /** * 随后,小明对“每一个”硬币分别进行 "一次" Q 操作。 * 对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转。 */ private static void qTurn (){ // 其中i和j为任意使操作可行的正整数 > 0,行号和列号都是从1开始。 for( int x = 1; x < qArray.length; x++ ){ for( int y = 1; y < qArray[x].length; y++ ){ for( int i = 1; i < qArray.length/x; i++ ){ for( int j = 1; j < qArray[x].length/y; j++ ){ // 对“每一个”硬币分别进行 "一次" Q 操作 qArray[i * x][j * y] = qArray[i * x][j * y] == 0 ? 1 : 0; } } } } } public static void main( String[] args ){ qTurn(); for(int i = 0; i < qArray.length; ++i){ System.out.println (Arrays.toString (qArray[i])); } System.out.println("当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。"); System.out.println("小明想知道最开始有多少枚硬币是反面朝上的。" + "于是,他向他的好朋友小M寻求帮助。\n" + "聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作," + "即可恢复到最开始的状态。然而小明很懒,不愿意照做。" + "于是小明希望你给出他更好的方法。帮他计算出答案"); System.out.println("\n由于0表示反面,1表示正面. \n所以: \n"); int count = 0; for(int i = 0; i < qArray.length; i++ ){ for(int j = 0; j < qArray[i].length; j++){ if(qArray[i][j] == 1){ count++; } } } System.out.println("最开始有 " + count + "枚硬币是反面朝上的"); }}
解题思路:
小明提供了一种算法,这里演示一下 n = 2,m = 3 矩阵的翻硬币过程(1 表示 正面, 0 表示反面)
1
1
1
1
1
1
-->(x , y) =(1 , 1)
x的倍数行,y的倍数列要翻转
0
0
0
0
0
0
-->(x , y) =(1 , 2)
x的倍数行,y的倍数列要翻转
0
1
0
0
1
0
-->(x ,y) = (1 , 3)
0
1
1
0
1
1
-->(x , y) = (2 , 1)
0
1
1
1
0
0
-->(x ,y) = (2 , 2)
0
1
1
1
1
0
-->(x ,y) = (2 , 3)
0
1
1
1
1
1
想必大家也看出了规律,我们令初始状态都为1,然后对每一个坐标进行操作,最后值为0个数的就是开始时反面的个数。本想用暴力,但是对于 n 和 m ,如此大的数肯定不行,所以我们继续分析还有什么其他的规律。
先看 n = 1 的情况:对于(1 , m),只要看它翻转的次数的奇偶就能确定它最终的状态(次数为偶数最后肯定为正面,次数为奇数最后肯定为反面)。因为当x = 1, 确定每次第一行都要参与翻转,当 m 是 y 的倍数的时候,(1 , m)就会翻转,所以(1 , m)全过程翻转的次数取决于 m 的约数个数,1 的约数个数为1 , 3 的约数个数为2, 5 的约数个数为2, 9 的约数个数为3。也就是说当 m = k^2 (k = 1 ,2 ,3···) 其约数个数为奇数,否则 其约数个数为偶数。 因为一般数约数都是成对出现,所以必为偶数,而一个数的平方数,有两个约数相等,所以要减一个相同的就变成了奇数。
所以,对于(1 , m)来说,若m = k^2(k = 1 ,2 ,3···)则最终状态为0,否则为1。
而最后0的个数总和就是我们要的结果
最后得出结论:对于n行m列矩阵,经过 Q 操作后 反面的次数 count = sqrt(n) * sqrt(m) ,(即取整后再相乘)。
AC代码:
#include <iostream>#include <string>#include <cstring>using namespace std;string multiply(string str1,string str2){//字符串相乘函数 string str = ""; //最终结果 int len1 = str1.size(),len2 = str2.size();//计算两个字符串的函数 int result[1000] = {0}; //开辟数组存乘积并初始化 for(int i = len1-1; i >= 0; --i){ for(int j = len2-1; j >= 0; --j){ result[i+j+1] += (str1[i]-'0')*(str2[j]-'0'); } } for(int i = len1+len2-1; i >= 1; --i){//让数组的每一位都是正确的数 result[i-1] = result[i-1]+result[i]/10; result[i] = result[i]%10; } int i = 0; while(result[i] == 0) ++i;//前导零不要 for(int j = i; j < len1+len2; ++j)//转成字符串形式 str += '0'+result[j]; return str;}int compare(string str1,string str,int pos){//字符串比较函数 int len1 = str1.size(); int len2 = str.size(); if(len2 > len1+pos) return 0; if(len2 < len1+pos) return 1; for(int i = 0; i < len2; ++i){ if(str1[i]-'0' > str[i]-'0') return 1; if(str1[i]-'0' < str[i]-'0') return 0; }}string strSqrt(string str){//对字符串开方取整函数 int len = str.size(); string str1 = "",str2 = ""; for(int i = 0; i < (len+1)/2; ++i){//若len为偶数,len/2=(len+1)/2;若len为奇数,len/2+1=(len+1)/2 for(int j = 0; j <= 9; ++j){//因为每一位的数值都是0~9 str1 = str2; str1 += '0'+j; if(compare(multiply(str1,str1),str,2*((len+1)/2-1-i)) == 1){ //由于str1后少了(len+1)/2-i-1个0,所以平方以后少了2*((len+1)/2-i-1)个 str2+=j-1+'0'; break; } if(j == 9) str2 += '9'; } } return str2;}int main(){ string n,m; cin>>n>>m; cout<<multiply(strSqrt(n),strSqrt(m))<<endl; return 0;}
- 蓝桥杯 历届试题 矩阵翻硬币(大数开方)
- 历届试题 矩阵翻硬币 大数开方
- 历届试题 矩阵翻硬币(大数开方)
- 历届试题 矩阵翻硬币 蓝桥杯 大数开方 大数相乘
- 蓝桥杯-历届试题-矩阵翻硬币-大数
- 矩阵翻硬币 蓝桥杯 大数开方 大数相乘
- 蓝桥杯历届试题 矩阵翻硬币
- 蓝桥杯 历届试题 矩阵翻硬币
- 蓝桥杯 历届试题 矩阵翻硬币
- 蓝桥杯 历届试题 矩阵翻硬币
- 蓝桥杯 历届试题 矩阵翻硬币
- 蓝桥杯 历届试题 矩阵翻硬币
- 蓝桥杯 历届试题 矩阵翻硬币
- Java蓝桥杯历届试题 矩阵翻硬币
- 历届试题 矩阵翻硬币(数学题,大数开根)
- 历届试题 矩阵翻硬币
- 历届试题 矩阵翻硬币
- 【历届试题】矩阵翻硬币
- LaTeX技巧005:改变文字的前景色和背景色
- HDU 5698 瞬间移动 (2016"百度之星" - 初赛(Astar Round2B) 1003)
- in exists 区别
- Java注解-3种基础注解
- 时间复杂度
- 蓝桥杯 历届试题 矩阵翻硬币(大数开方)
- 金匮要略白话文
- 软件构架求助
- 抽象类和接口区别
- Spring核心技术(五)——Spring中Bean的作用域
- Javaweb项目虚拟路径
- C语言文件输入输出总结
- Activity的生命周期和四种开启模式之我见
- BZOJ 4563: [Haoi2016]放棋子