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

来源:互联网 发布:利驰软件怎么样 编辑:程序博客网 时间: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;}
0 0
原创粉丝点击