二维格雷码是在格雷码的基础上延伸出的新的算法题
要搞懂这个题目,首先要会求格雷码,我所使用的是分治法
我们只要在函数递归中不断将数组对分,直到k=pow(2,1)时停止递归,并为格雷码赋值即可。我用二维数组表示格雷码,a[n][m]表示第n+1个二进制码的第m+1位。函数如下:
void dc(int k,int i)
{
if (i != 1)
{
dc(k / 2, i - 1);
for (int q = 0; q < k / 2; q++)
{
a[q][i-1] = 0;
}
for (int x = k / 2; x < k; x++)
{
a[x][i-1] = 1;
for (int y = 0; y < i-1 ; y++)
{
a[x][y] = a[k - x - 1][y];
}
}
}
else
{
a[0][0] = 0;
a[1][0] = 1;
}
}
我们通过分治算法得到格雷码后,就要继续求二维格雷码。二维格雷码无外乎多了一个维度,但我们想要同样使用分治算法直接求得二维格雷码是行不通的,因为分治算法求出来的格雷码只能是一维的一组数。不过我们可以将已经求出的一维格雷码看成表格中的两个维度,组合成二维格雷码,以题目中给出的4*5的二维格雷码为例:
乍一看好像是那么回事,但仔细看的话会发现“这不是和题目中的例子不一样吗?”确实,例子中的0是在第二列,而这个表的0是在第一列,而这个表也确实是错的,因为我们要求的是“最小代表元”,而第五列的“110”其实不是最小的,我们回过头看k=8,i=3时的格雷码:
将后半部分的格雷码前移后,末尾的“100”与“000”依然满足关系,而且倒数第一第二的“100”和“101”都是比“110”和“111”要小的,所以在行或列的一维格雷码数不是正好等于pow(2,i)时,我们要将后半部分上移。正确的表是:
然后求最小代表元,只要找到 一维格雷码最大的行和列即可。
接下来是最后一步——将两个一维格雷码相连。这里可能出现两个结果,一个是行在前列在后,另一个是列在前行在后,我们要对其比较。虽然代表元是最大值,但我们要求的是最小代表元,搞清逻辑关系后会明白在比较后要取较小的值。
全部代码为:
#include<stdio.h>
#include<math.h>
int a[100][100];
int b[100];
void dc(int k, int i);
int main()
{
int t,v,r,u;
int m, n, kn, in, im, km, max = 0, max_fn, max_fm, result, result1, result2;
scanf_s("%d%d", &m, &n);
if (m > n)
{
int t;
t = m;
m = n;
n = t;
}//m表示行,n表示列,使n>m,便于计算
for (in = 0; pow(2, in) < n; in++);
kn = pow(2, in);//kn是求比n大而且是2的整数次方的第一个数,in是这个整数次方
for (im = 0; pow(2, im) < m; im++);
km = pow(2, im);
dc(kn,in);//kn大于等于km,所以传kn
for (t = 0; t <kn; t++)
{
for (r = 0, v = 1; r < in; r++, v = v * 2)
b[t] = b[t] + a[t][r] * v;
}//将格雷码转换为十进制数
if (kn != n)
{
for (t = kn - 1, u = n - kn / 2; u > 0; t--,u--)//替代了将后半格雷码前移的操作
//因为后半段格雷码一定大于前半段,所以直接从末尾开始找最大的格雷码,
{
if (max < b[t])
{
max = b[t];
max_fn = t;//标记行中最大的格雷码所在的位置
}
}
}
else
{
for (t = 0; t < kn; t++)
{
if (max < b[t])
{
max = b[t];
max_fn = t;
}
}
}
max = 0;
if (km != m)
{
for (t = km - 1, u = n - km / 2; u > 0; t--, u--)
{
if (max <= b[t])
{
max = b[t];
max_fm = t;//标记列中最大的格雷码所在的位置
}
}
}
else
{
for (t = 0; t < km; t++)
{
if (max <= b[t])
{
max = b[t];
max_fm = t;
}
}
}
result1 = b[max_fm] * pow(2,in) + b[max_fn];//m在前n在后的代表元
result2 = b[max_fn] * pow(2,im) + b[max_fm];//n在前m在后的代表元
result = (result1 < result2) ? result1 : result2;//在两个代表元中求出最小代表元
printf("%d", result);
return 0;
}
void dc(int k,int i)
{
if (i != 1)
{
dc(k / 2, i - 1);
for (int q = 0; q < k / 2; q++)
{
a[q][i-1] = 0;
}
for (int x = k / 2; x < k; x++)
{
a[x][i-1] = 1;
for (int y = 0; y < i-1 ; y++)
{
a[x][y] = a[k - x - 1][y];
}
}
}
else
{
a[0][0] = 0;
a[1][0] = 1;
}
}