二进制高精度GCD模拟题。题意:你有一块长和宽都是整数的矩形土地,作为市长,你需要把这块土地分割成很多块边长相等的正方形土地,并且分割完后土地不能有剩余,而且正方形的边长要尽量大。现在以二进制数的方式来告诉你这块土地的长和宽,请你也用二进制数的形式来表示正方形的边长。
我的解题思路:首先分析题意必须要知道其实这就是求矩形土地长和宽的最大公约数,这个最大公约数就是正方形的边长。其次,由于长和宽的范围是不大于2^1000,说明这个二进制数的长度可以达到一千,就算转换为十进制数,也没有任何数据类型可以存得下,所以这里要用到高精度。最后,求二进制数的GCD有这样三个性质(假设a和b都是二进制数):
1.如果a和b都是偶数,那么gcd(a, b) = 2 * gcd(a>>1, b>>1);
2.如果a是奇数、b是偶数,那么gcd(a, b) = gcd(a, b>>1);
3.如果a和b都是奇数且a>b,那么gcd(a, b) = gcd(a-b, b);
不要问我如果a和b都是奇数且a和b相等怎么办了,相等的话他们本身就是最大公约数了。有了以上的公式,模拟就可以写了。
我的解题代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <list>
#include <map>
using namespace std;
#define N 1005
char l[N], w[N]; //两个二进制数
int ln, wn; //两个二进制数的长度
int o = 0; //输出答案时需要添加的后缀0的数量
int t; //样例数
void DataProcess(); //计算并输出二进制最大公约数
void L(); //计算l -= w;
void W(); //计算w -= l;
int main()
{
int tn = 1;
scanf("%d", &t);
while (t--)
{
scanf("%s %s", l, w);
o = 0;
ln = strlen(l);
wn = strlen(w);
printf("Case #%d: ", tn++);
if ((ln == 1 && l[0] == '1') || (wn == 1 && w[0] == '1')) //特例直接输出
{
puts("1");
continue;
}
DataProcess();
}
return 0;
}
void DataProcess()
{
while (ln != wn || strncmp(l, w, ln) != 0) //当两数不相等时,求其最大公约数
{
if (l[ln-1] == w[wn-1] && l[ln-1] == '0' && ln > 0 && wn > 0) //都是偶数
{
o++; //gcd(l,w) = 2 * gcd(l,w),所以最终输出边长时要添加0
ln--; //相当于右移一位
wn--;
}
else if (l[ln-1] != w[wn-1] && ln > 0 && wn > 0) //一奇一偶
{
if (l[ln-1] == '0') ln--;
else wn--;
}
else if (l[ln-1] == w[wn-1] && l[ln-1] == '1' && ln > 0 && wn > 0) //都是奇数
{
if (ln == wn)
{
if (strncmp(l, w, ln) > 0) L(); //l > w
else W(); //w > l
}
else if (ln > wn) L();
else W();
}
}
for (int i=0; i<ln; ++i) putchar(l[i]);
for (int i=0; i<o; ++i) putchar('0');
putchar('\n');
return;
}
void L()
{
int i = ln - 1;
int j = wn - 1;
int k;
while (j != -1)
{
l[i] -= w[j] - '0';
if (l[i] < '0')
{
l[i-1] -= 1; //借位
l[i] += 2;
}
i--;
j--;
}
for (; i>=0; --i)
{
if (l[i] < '0') //借位
{
l[i] += 2;
l[i-1] -= 1;
}
}
for (k=0; k<ln; ++k)
{
if (l[k] == '0') continue;
break;
}
for (i=0; i<ln-k; ++i) //相减后的数因为可能有前缀0,所以要向前移
{
l[i] = l[k+i];
}
ln -= k; //相减后的长度
return;
}
void W() //与L()基本相同
{
int j = ln - 1;
int i = wn - 1;
int k;
while (j != -1)
{
w[i] -= l[j] - '0';
if (w[i] < '0')
{
w[i] += 2;
w[i-1] -= 1;
}
i--;
j--;
}
for (; i>=0; --i)
{
if (w[i] < '0')
{
w[i] += 2;
w[i-1] -= 1;
}
}
for (k=0; k<wn; ++k)
{
if (w[k] == '0') continue;
break;
}
for (i=0; i<wn-k; ++i)
{
w[i] = w[k+i];
}
wn -= k;
return;
}