【蓝桥杯题解】矩阵翻硬币
来源:互联网 发布:利驰软件怎么样 编辑:程序博客网 时间:2024/05/22 05:18
历届试题 矩阵翻硬币
时间限制:1.0s 内存限制:256.0MB
提交此题
问题描述
小明先把硬币摆成了一个 n 行 m 列的矩阵。
随后,小明对每一个硬币分别进行一次 Q 操作。
对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转。
其中i和j为任意使操作可行的正整数,行号和列号都是从1开始。
当小明对所有硬币都进行了一次 Q 操作后,他发现了一个奇迹——所有硬币均为正面朝上。
小明想知道最开始有多少枚硬币是反面朝上的。于是,他向他的好朋友小M寻求帮助。
聪明的小M告诉小明,只需要对所有硬币再进行一次Q操作,即可恢复到最开始的状态。然而小明很懒,不愿意照做。于是小明希望你给出他更好的方法。帮他计算出答案。
输入格式
输入数据包含一行,两个正整数 n m,含义见题目描述。
输出格式
输出一个正整数,表示最开始有多少枚硬币是反面朝上的。
样例输入
2 3
样例输出
1
数据规模和约定
对于10%的数据,n、m <= 10^3;
对于20%的数据,n、m <= 10^7;
对于40%的数据,n、m <= 10^15;
对于100%的数据,n、m <= 10^1000(10的1000次方)
题目最终转化求sqrt(n)*sqrt(m)(开方出来取整),由于题目数据太大(10的1000次方),因此实际考点是大数的开方和乘法:
大数乘法:模拟笔算即可
/*大数相乘函数*知识点1:m位数和n位数相乘最多有n+m位数*步骤1.移位操作*步骤2.进位操作*步骤3.去除高位0*步骤4.转化为字符串*/string strMul(string a, string b){ int len1 = a.length(); int len2 = b.length(); int i, j; vector<int>num(len1 + len2, 0);//最多len1+len2位 //步骤一 //遍历相乘,i+j相等的要相加,暂存在num中,num中由低位到高位存储,方便进位 for (i = 0; i<len1; i++) for (j = 0; j<len2; j++) { num[len1 - 1 + len2 - 1 - i - j] += (a[i] - '0')*(b[j] - '0'); } //步骤二 //进位操作,由低位向高位进位 for (i = 0; i<len1 + len2 - 1; i++) { num[i + 1] = num[i + 1] + num[i] / 10; num[i] = num[i] % 10; } //步骤三 //去除高位的多余的零 i = len1 + len2 - 1; while (i>=0) { if (num[i] != 0) break; i--; } //步骤四 //将结果转化成字符串 string result = ""; while (i>=0) { result += num[i--] + '0'; } return result;}
大数开方:
/**知识点1:对n位数开方,开方出来的数的位数:n是偶数为n/2位,n是奇数为n/2+1位;*知识点2:大数比较(见后面)*步骤1:确定开方出来的数的位数*步骤2:从高位向低位确定每一位的值*/string StrQurt(string a){ int len = a.length(); //对n位数开方,开方出来的数的位数:n是偶数为n/2,n是奇数为n/2+1; len = len % 2 == 0 ? len / 2 : len / 2 + 1; string ans = ""; for (int i = 0; i < len;i++) { ans +='0'; int j; //确定当前位从0-9遍历判断 for (j = 0; j <= 9;j++) { ans[i] = j + '0'; int key = StrCompare(StrMul(ans + string(len - 1 - i, '0'), ans + string(len - 1 - i, '0')), a); if (key == 0)return ans + string(len - 1 - i, '0'); else if (key==1)break; } if (j<10) ans[i]--; } return ans;}
/*大数比较函数*a>b return 1*a<b return -1*a=b return 0*/int StrCompare(string a,string b){ if (a.length() < b.length()) return -1; else if (a.length() > b.length()) return 1; else return a.compare(b);}
题目到此基本已经没有多大问题了,但是在确定开方数的每一位值时会花费大量的时间,因为后面有多个零做乘法,这是非常耗时的,提供两个方法优化:
一、优化乘法算法(网上有很多,自己找)
二、将零相乘的部分剔除开来,重新写一个新的比较函数,传入比较函数有三个参数StrCmp(已经确定的高位的值得乘方,2*未确定的低位0的个数,另一个要比较的数 )
//大数比较函数int strCmp(string a, int pos, string b){ int i; if (a.length() + pos>b.length()) return 1; if (a.length() + pos<b.length()) return -1; if (a.length() + pos == b.length()) { for (i = 0; i<b.length(); i++) { if (i < a.length()) { if (a[i]<b[i]) return -1; if (a[i] == b[i]) continue; if (a[i]>b[i]) return 1; } else { if (b[i]>'0')return -1; } } } return 0;}
相应的开方函数为:
//大数开方函数/**知识点1:对n位数开方,开方出来的数的位数:n是偶数为n/2位,n是奇数为n/2+1位;*知识点2:大数比较*步骤1:确定开方出来的数的位数*步骤2:从高位向低位确定每一位的值*/string StrQurt(string a){ int len = a.length(); //对n位数开方,开方出来的数的位数:n是偶数为n/2,n是奇数为n/2+1; len = len % 2 == 0 ? len / 2 : len / 2 + 1; string ans = ""; for (int i = 0; i < len;i++) { ans +='0'; int j; //确定当前位从0-9遍历判断 for (j = 0; j <= 9;j++) { ans[i] = j + '0'; int key = strCmp(StrMul(ans, ans),2*(len-1-i),a); if (key == 0)return ans + string(len - 1 - i, '0'); else if (key==1)break; } if (j<10) ans[i]--; } return ans;}
完整代码如下:
#include<iostream>#include<string>#include<cstring>#include <vector>using namespace std;/////*/*大数相乘函数*知识点1:m位数和n位数相乘最多有n+m位数*步骤1.移位操作*步骤2.进位操作*步骤3.去除高位0*步骤4.转化为字符串*/string StrMul(string a, string b){ int len1 = a.length(); int len2 = b.length(); int i, j; vector<int>num(len1 + len2, 0);//最多len1+len2位 //步骤一 //遍历相乘,i+j相等的要相加,暂存在num中,num中由低位到高位存储,方便进位 for (i = 0; i<len1; i++) for (j = 0; j<len2; j++) { num[len1 - 1 + len2 - 1 - i - j] += (a[i] - '0')*(b[j] - '0'); } //步骤二 //进位操作,由低位向高位进位 for (i = 0; i<len1 + len2 - 1; i++) { num[i + 1] = num[i + 1] + num[i] / 10; num[i] = num[i] % 10; } //步骤三 //去除高位的多余的零 i = len1 + len2 - 1; while (i>=0) { if (num[i] != 0) break; i--; } //步骤四 //将结果转化成字符串 string result = ""; while (i>=0) { result += num[i--] + '0'; } return result;}//大数比较函数int strCmp(string a, int pos, string b){ int i; if (a.length() + pos>b.length()) return 1; if (a.length() + pos<b.length()) return -1; if (a.length() + pos == b.length()) { for (i = 0; i<b.length(); i++) { if (i < a.length()) { if (a[i]<b[i]) return -1; if (a[i] == b[i]) continue; if (a[i]>b[i]) return 1; } else { if (b[i]>'0')return -1; } } } return 0;}//大数开方函数/**知识点1:对n位数开方,开方出来的数的位数:n是偶数为n/2位,n是奇数为n/2+1位;*知识点2:大数比较*步骤1:确定开方出来的数的位数*步骤2:从高位向低位确定每一位的值*/string StrQurt(string a){ int len = a.length(); //对n位数开方,开方出来的数的位数:n是偶数为n/2,n是奇数为n/2+1; len = len % 2 == 0 ? len / 2 : len / 2 + 1; string ans = ""; for (int i = 0; i < len;i++) { ans +='0'; int j; //确定当前位从0-9遍历判断 for (j = 0; j <= 9;j++) { ans[i] = j + '0'; int key = strCmp(StrMul(ans, ans),2*(len-1-i),a); if (key == 0)return ans + string(len - 1 - i, '0'); else if (key==1)break; } if (j<10) ans[i]--; } return ans;}int main(){ string a, b; while (cin >> a>>b) { cout << StrMul(StrQurt(a),StrQurt(b)) << endl; } return 0;}
- 【蓝桥杯题解】矩阵翻硬币
- 矩阵翻硬币 蓝桥杯
- 蓝桥杯 矩阵翻硬币
- 蓝桥杯 矩阵翻硬币
- 蓝桥杯-矩阵翻硬币
- 矩阵翻硬币蓝桥杯
- 矩阵翻硬币 蓝桥杯
- 蓝桥杯 矩阵翻硬币
- 蓝桥杯-矩阵翻硬币
- 蓝桥杯 矩阵翻硬币
- 蓝桥杯--矩阵翻硬币
- 蓝桥杯历届试题 矩阵翻硬币
- 蓝桥杯——矩阵翻硬币
- 历年试题 矩阵翻硬币 (蓝桥杯)
- 蓝桥杯 历届试题 矩阵翻硬币
- 蓝桥杯 历届试题 矩阵翻硬币
- 蓝桥杯 历届试题 矩阵翻硬币
- 蓝桥杯题目10矩阵翻硬币
- 函数作为参数
- POJ 1835 宇航员 中文
- Real adaboost
- 【算法】组中元素全是1~n之间的整数问题
- ajax(四)
- 【蓝桥杯题解】矩阵翻硬币
- ARM体系结构(一)
- 【zabbix】利用LLD自动发现功能监控多Memcached实例
- 随笔
- 爬取微博用户的原创微博
- linux查看进程启动时间
- Gym - 101147H H. Commandos DAG
- spring rmi
- POJ 3218 文本对齐 中文