【算法篇】二维格雷码(全网首发,大概?)

二维格雷码是在格雷码的基础上延伸出的新的算法题

要搞懂这个题目,首先要会求格雷码,我所使用的是分治法

我们只要在函数递归中不断将数组对分,直到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;
	}
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值