历届试题 矩阵翻硬币

----------------------------------------------------痞子小小崔
时间限制:1.0s 内存限制:256.0MB

问题描述
  小明先把硬币摆成了一个 n 行 m 列的矩阵。
  随后,小明对每一个硬币分别进行一次 Q 操作。
  对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转。
  其中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次方)。
  
首先看到 10的一千次方 我不淡定了,这肯定得涉及大数问题。
如何实现乘法和开方,大数的存储,我们使用string,这样才能存进去

大数处理:

乘法:其实有很多速度快而且更巧妙的大数乘法,但是在蓝桥杯,我们只要使用模拟手算法应该是问过,虽说是模拟,但也有一些我们常用但不太了解的规律,我们在手算乘法的时候,需要进行移位和进位,这两个操作我们会通过两步完成
1.移位:对于两个数a=12,b=25,在相乘的时候我们让每一位数分别相乘,即a[0]*b[0]=2 , a[0]*b[1]=5 , a[1]*b[0]=4 , a[1]*b[1]=10,那么规律就是,对于两个数a[i] ,
b[j],所有i+j相同的数都要加到一起,所以我们要把5+4=9合并为一个数,也就是说,将每一位数相乘之后,我们实际得到了2,9,10三个没有进位的数
2.进位:通过上面的操作,我们的2,9,10三个没有进位的数,下面就要进行进位,因为我们是把高位相乘得到的数放在前面,地位相乘得到的数放在后面,所以这三个数排列自然也是高位在前,地位在后,所以要从右向左进位,进位的方法就是**a[i]=a[i+1]/10
,
a[i+1]=a[i+1]%10,**如果放到这三个数上面,就是,9+10/10=10,然后10%10=0,这三个数变成2,10,0,再一次就是2+10/10=3,10%10=0,三个数变为3,0,0,而这正是我们要求的结果,在实际操作中,我们习惯于将数倒着存放,即将数存为10,9,2,这是为了进位方便,因为如果正序的话如果最高位发生进位我们就要将每一个数向后移动一位从而挪出一个空位,想想都麻烦,倒序的话因为是向后进位,想怎么进就怎么进

开方:假如一个数有偶数位n,那么这个数的方根有n/2位;如果n为奇数,那方根为(n+1)/2位。

枚举出它的整数根
0000=0<1200
10
10=100<1200
2020=400<1200
30
30=900<1200
4040=1600>1200
所以,这个根的十位就是3,然后,再枚举个位
31
31=961<1200
3333=1089<1200
34
34=1156<1200
35*35=1225>1200
我先用暴力算,先找到规律,解决小数上的问题,然后再去解决大数问题

我首先做的就是 暴力找规律
以下为暴力找规律的代码:

//这种暴力解简直了,500X200已经达到21秒了。。。
#include<iostream>
#include<string.h>
using namespace std;int M[500][200];
main()
{int n,m,dep=0;cin>>n>>m;cout<<endl;cout<<"布局方正:"<<endl; memset(M,0,sizeof(M)); //memset 函数方便全部置0 所以我用0代表1 反过来 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){for(int k=1;k<=n;k++)for(int l=1;l<=m;l++){if(i*k<=n&&j*l<=m) M[i*k][j*l]=!M[i*k][j*l];}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cout<<"  "<<M[i][j]; if(M[i][j]==1) dep++;}cout<<endl;}      cout<<"个数:"<<dep;return 0;
}

行数始终为1时
(1,4) 1001
(1,9) 100100001
(1,16)1001000010000001
(1,25)1001000010000001000000001
列数始终为1时
(4,1) 1001
(9,1) 100100001
(16,1)1001000010000001
(25,1)1001000010000001000000001
发现了吗!! 对是数字的平方位上有翻转的硬币
那么25X25上,行上有5个,列上有5个,总计就是5*5=25个。

现在规律解决了:输入2个数 分别求2个数的根,然后求介于哪两个数平方之间,得出行与列上有几个是翻转的,然后两者相乘,就是结果!

然后代码功能:1、求根。2、求大数之间相乘。3、找出求出的根介于哪两数之间。
我也在网上找到了不错的代码,稍微修改了一丢丢(把原来判断用的“:”条件改了),但没有注释(也许是我没找到吧),然后我好不容易看懂并加上注释,好心累,不写注释真的大丈夫?????大家也可以看看(还看不懂的,到最下面,我打了一份带输出的代码,结合注释看,再看不懂,GG)

#include<iostream>
#include<string>
#include<cstring>
using namespace std;string strMul(string a,string b)//strmul(两数相乘) 求a与b的乘
{//a为45 b为45//a[0]=4 a[1]=5//b[0]=4 b[1]=5////a[0]*b[0]=16//a[0]*b[1]=20     //a[1]*b[0]=20      i+j=1 相同  20+20=40 //a[1]*b[1]=25////25 25%10=5(二)    | |5                  | |5                |5 | //40 40+25/10=42(一)| |42 42%10=2(二)    | |2                |2 |//16                 |>|16 16+42/10=20(一)|>|20 20%10=0(二)  |0 |..//0                  | |0                  | |0  0+20/10=2(一)|2 |..//0                  | |0                  | |0                |0 |..//0                  | |0                  | |0                |0 |..//.....string result="";int len1=a.length();int len2=b.length();int i,j;int num[500]={0};    //用来存每个 位 上的数  //a[0]*b[0]=16//a[0]*b[1]=20     //a[1]*b[0]=20      i+j=1 相同  20+20=40 //a[1]*b[1]=25for(i=0;i<len1;i++)//i与j相同的相加  并将25 40 16存入num  for(j=0;j<len2;j++){num[len1-1+len2-1-i-j]+=(a[i]-'0')*(b[j]-'0'); }//25 25%10=5(二)    | |5                  | |5                |5 | //40 40+25/10=42(一)| |42 42%10=2(二)    | |2                |2 |//16                 |>|16 16+42/10=20(一)|>|20 20%10=0(二)  |0 |..//0                  | |0                  | |0  0+20/10=2(一)|2 |..//0                  | |0                  | |0                |0 |..//0                  | |0                  | |0                |0 |..//.....
i=0;i<len1+len2;i++)//进位  得出两数相乘结果  不过也许会有多余0的存在 {num[i+1]=num[i+1]+num[i]/10;num[i]=num[i]%10;}//下面2个for是一起作用的 第一个for排除其他的'0',得到num[3]=2中的i为3   然后传给第二个for for(i=len1+len2-1;i>=0;i--){if(num[i]!=0)break;}for(;i>=0;i--)//第二个for  将num里的2025数字转变为字符串 送到result中 {result+=(char)(num[i]+'0');//num为int型 所以这里用char  需要加入'0'  }return result; //为a与b的乘
}int strCmp(string a,string b,int pos)//strCmp(目前求出的根^2 , n或m , ''n或m'' 的长度与 ''目前求出的根^2'' 的长度的相差位数)
{int i;//假设 a为9 b为858 pos为2 //1+2==3//9>8//返回1  //根据位数 判断大小 (比如:23<223 一定) if(a.length()+pos>b.length())  return 1;        //返回1 跳出while    if(a.length()+pos<b.length())     return 0;        //返回0 继续while //当长度相同 开始比较每一位上的数来判断大小 if(a.length()+pos==b.length()){for(i=0;i<a.length();i++)//从第一位开始 {if(a[i]<b[i]) return 0;if(a[i]==b[i]) continue;if(a[i]>b[i]) return 1;}}
}string strSqrt(string a)
{string result=""; //设立result字符串为空  此时输出什么也没有  0也没有 int i;int len=a.length();if(len%2==0) len=len/2;else len=len/2+1;//a长度的根长度(理论值)   for(i=0;i<len;i++)//求根的关键 i代表位数 从第一位开始(i从0开始) 然后第二位。。!!!最后一位不在这里循环 在里面的                                        {result+='0';//strCmp(目前求出的根^2 , n或m , ''n或m'' 的长度与 ''目前求出的根^2'' 的长度的相差位数) while(strCmp(strMul(result,result),a,2*(len-1-i))!=1){result[i]++;if(result[i]==':')  break;  //'9'加1为':'  一旦到10 便退出 }result[i]--;//消除while条件判断的影响 }return result;//strsqrt(求根)
}int main()
{string n,m;cin>>n>>m;//strmul(两数相乘)、strsqrt(求根) cout<<strMul(strSqrt(n),strSqrt(m))<<endl;return 0;
}

下面下面呢!! 就是带输出的代码了,结合注释, 看懂的人可以忽略不计了!!!!

#include<iostream>
#include<string>
#include<cstring>
using namespace std;string strMul(string a,string b)
{string result="";                                               cout<<"      "<<"进入strMul"<<"  运行"<<"strMul("<<a<<","<<b<<")"<<"  result="<<result<<endl;int len1=a.length();                                         int len2=b.length();                                           cout<<"        "<<"len1:"<<len1<<"   len2:"<<len2<<endl<<endl;int i,j;int num[500]={0};cout<<"        "<<"进入for循环"<<endl; for(i=0;i<len1;i++)for(j=0;j<len2;j++){cout<<"        "<<"第一个循环: i="<<i<<" j="<<j<<endl; cout<<"        "<<"a["<<i<<"]="<<a[i]<<"  "<<"b["<<j<<"]="<<b[j]<<endl; num[len1-1+len2-1-i-j]+=(a[i]-'0')*(b[j]-'0');cout<<"    "<<"num["<< len1-1+len2-1-i-j<<"]="<<num[len1-1+len2-1-i-j]<<endl;cout<<"      "<<"a["<<i<<"]="<<a[i]<<"  "<<"b["<<j<<"]="<<b[j]<<endl; }cout<<endl;for(i=0;i<len1+len2;i++){cout<<"        "<<"第二个循环: i="<<i<<endl;num[i+1]=num[i+1]+num[i]/10;                             cout<<"        num[i+1]="<<num[i+1]<<endl;num[i]=num[i]%10;                                         cout<<"        num[i]="<<num[i]<<endl;}cout<<endl;for(i=len1+len2-1;i>=0;i--){cout<<"        "<<"第三个循环: i="<<i<<endl;cout<<"        num[i]="<<num[i]<<endl;if(num[i]!=0)break;}cout<<endl;for(;i>=0;i--){cout<<"        "<<"第四个循环: i="<<i<<endl;cout<<"        result="<<result<<endl; result+=(char)(num[i]+'0');cout<<"        result="<<result<<endl<<endl; }cout<<"        "<<"result:"<<result<<endl<<endl; return result;
}int strCmp(string a,string b,int pos)
{int i;cout<<"      "<<"进入strCmp:   "<<"  运行strCmp("<<a<<","<<b<<","<<pos<<")"<<endl;cout<<"        "<<"a.length()+pos="<<a.length()+pos<<"    b.length()="<<b.length()<<endl;if(a.length()+pos>b.length())                                   {cout<<"        返回 1"<<endl;return 1;} if(a.length()+pos<b.length())                                  {cout<<"        返回 0"<<endl;return 0;}if(a.length()+pos==b.length()){for(i=0;i<a.length();i++){cout<<"        "<<"    进入循环 i="<<i<<endl; cout<<"          a[i]="<<a[i]<<" b[i]="<<b[i]<<endl;if(a[i]<b[i])                                           {cout<<"          返回 0"<<endl;return 0;}if(a[i]==b[i])                                          {cout<<"          continue"<<endl;continue;}if(a[i]>b[i])                                          {cout<<"          返回 1"<<endl;return 1;}}}
}string strSqrt(string a)
{cout<<"进入strSqrt  a为: "<<a<<endl;string result=""; int i;int len=a.length();if(len%2==0) len=len/2;else len=len/2+1;                                              cout<<"  "<<"a长度的根的理论长度 len: "<<len<<endl;for(i=0;i<len;i++)                                     {cout<<"  "<<"strSqrt的第"<<i<<"次循环" <<endl; result+='0';                                              cout<<"    "<<"result: "<<result<<endl<<endl;cout<<"    "<<"运行while(!=1)判断 "<<"a="<<a<<"  2*(len-1-i)(pos)="<<2*(len-1-i)<<endl;cout<<"    "<<"while(strCmp(strMul("<<result<<","<<result<<")"<<","<<a<<","<<2*(len-1-i)<<")"<<endl;while(strCmp(strMul(result,result),a,2*(len-1-i))!=1){cout<<"    "<<"进入while"<<endl; cout<<"      "<<"pos="<< 2*(len-1-i)<<endl;cout<<"      "<<"result:"<<result<<"   result["<<i<<"]="<<result[i]<<endl;result[i]++;if(result[i]==':')  break;cout<<"      "<<"result:"<<result<<"   result["<<i<<"]="<<result[i]<<endl<<endl;cout<<"    "<<"运行while(!=1)判断 "<<"a="<<a<<"  2*(len-1-i)(pos)="<<2*(len-1-i)<<endl;cout<<"    "<<"while(strCmp(strMul("<<result<<","<<result<<")"<<","<<a<<","<<2*(len-1-i)<<")"<<endl;}cout<<"    "<<"出while"<<endl;result[i]--;cout<<"    "<<"result:"<<result<<"   result["<<i<<"]="<<result[i]<<endl<<endl;}return result;
}int main()
{string n,m;cin>>n>>m;cout<<strMul(strSqrt(n),strSqrt(m))<<endl;return 0;
}

输出的样子

【蓝桥杯】历届试题 矩阵翻硬币相关推荐

  1. 蓝桥杯历届试题----矩阵翻硬币

    矩阵翻硬币 问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵.随后,小明对每一个硬币分别进行一次 Q 操作.对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进 ...

  2. 蓝桥杯 历届试题 矩阵翻硬币(大数)

    问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第x行第y列的硬币进行 Q 操作的定义:将所有第 i*x 行,第 j*y 列的硬币进行翻转. ...

  3. Java实现 蓝桥杯 历届试题 矩阵翻硬币

    问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转. 其 ...

  4. 蓝桥杯历届试题:翻硬币——Java实现

    题目: 以下是输入输出格式: 这里我也没多想,看到了输入的字符串不是固定的,但是我们需要对其进行元素的操作,马上就想到了Java中字符串和字符数组之间的转换--toCharArray()方法. 以下是 ...

  5. 历届试题 矩阵翻硬币 蓝桥杯 大数开方 大数相乘

    历届试题 矩阵翻硬币   时间限制:1.0s   内存限制:256.0MB 问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第x行第y列的硬 ...

  6. 历届试题 矩阵翻硬币

    历届试题 矩阵翻硬币   时间限制:1.0s   内存限制:256.0MB      问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第x行 ...

  7. 【蓝桥杯题解】矩阵翻硬币

    历届试题 矩阵翻硬币 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第x行第y列的 ...

  8. 试题 历届试题 矩阵翻硬币

    原题链接:试题 历届试题 翻硬币 1.懒得样式,截图如下 2.思路: 首先,同时翻动两个相邻的硬币,这就意味着不同的硬币的个数一定是偶数个,如果是奇数个永远也达不到目标状态. 其次,顺序翻转就是翻转次 ...

  9. 蓝桥 历届试题 矩阵翻硬币 JAVA

    问题描述 小明先把硬币摆成了一个 n 行 m 列的矩阵. 随后,小明对每一个硬币分别进行一次 Q 操作. 对第x行第y列的硬币进行 Q 操作的定义:将所有第 ix 行,第 jy 列的硬币进行翻转. 其 ...

最新文章

  1. WebVie打开woffice文档
  2. canvas学习绘制扇形
  3. 7-26晚上实现mystring
  4. 2017蓝桥杯决赛-发现环 数据结构|搜索
  5. 大数据如何促进经济增长?中国优势及应对 | 互联网经济学
  6. 1.7 理解 Dropout
  7. 【Oracle】ORA-04031错误解决
  8. 产品管理工作的文档管理
  9. Android 用户可以直接在搜索页面上安装 app 了
  10. 游戏使用html签名,利用HTML5实现电子签名板文字涂鸦代码
  11. DO56 物流信息网
  12. 在线画图工具,高效办公
  13. 短视频解析 MD5修改 ,为什么要修改MD5
  14. 全概率公式和贝叶斯公式
  15. Navicat 恢复mysql psc 备份文件
  16. LODOP直接用base64码输出图片
  17. Python-身体质量指数BMI
  18. Android Studio使用技巧
  19. Flying Saucer 不支持中文,换行,粗体,CheckBox多选框的解决方案
  20. 【每日一读】Large Scale Network Embedding: A Separable Approach

热门文章

  1. UIApplicationOpenSettingsURLString闪退
  2. Android中WebView与JS的交互
  3. 基于jsp+ssm+java的干洗店洗衣管理系统-计算机毕业设计
  4. Android 源码分析 (十一) ContentProvider 启动
  5. xingtai -究极炸弹
  6. HTML/XML转义字符对照表(建议收藏)
  7. 音箱有杂音怎么办?教你如何解决音箱有杂音
  8. 世界视频编码器大赛结果揭晓,腾讯V265编码器勇夺两项第一
  9. 小狐狸ChatGPT付费创作系统1.6.1独立开源版 + 小程序VUE前端
  10. ECCV 2022 | 浙大提出:基于骨骼点的少样本动作识别