字节2048笔试题
题目描述
2048的规则就不啰嗦了,值得一提的是笔试题里不需要随机产生新的数字
输入描述
输入第一行是用户按下的方向键,1 2 3 4 分别表示上 下 左 右
接下来是一个4*4的矩阵,空格分割,0代表该位置没有数字
输出描述
输出用户按下该方向键后的矩阵数字,忽略随机产生数字
示例1
输入
3
2 0 4 8
0 2 0 2
0 0 2 0
2 0 2 8
输出
2 4 8 0
4 0 0 0
2 0 0 0
4 8 0 0
解题思路
核心思路是写出4个方向的合并函数,但实际上我们可以只写任意方向的合并函数(这里以左为例),其他方向的操作只需要对矩阵做一定的旋转即可。
Tip: 比如向右旋转
只需要先把矩阵旋转180度
,然后向左合并
,再旋转180度
即可得到向右合并的结果
旋转函数定义:
void rotate(int jz[][N],int times)
{
int tmp[N][N];
memcpy(tmp,jz,sizeof(tmp));
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
jz[i][j] = tmp[N-1-j][i];
}
}
if(times>1)
rotate(jz,times-1);
}
左合并函数定义:
void mergeLeft(int jz[][N])
{
for(int l = 0;l<N;l++) //循环处理每一行
{
int *line = jz[l];
int j,k;
for(int i=0;i<N;i++)
{
j = i;
while(!line[j] && j<N) //从当前位置开始找到第一个不为空的位置
j++;
k=j+1;
while(!line[k] && k<N) //找到下一个不为空的数字
k++;
if(k<N && line[j] == line[k]) //判断两个数能否合并
{
line[j] = 0;
line[i] = 2 * line[k]; // i和j有可能相等,所以使用k
line[k] = 0;
}
else if(j<N) //移动
{
int tmp = line[j]; //i和j可能相等
line[j] = 0;
line[i] = tmp;
}
else
{
break;//右边已经没有数字了
}
}
}
}
合并函数
void merge(int jz[][N],int dir)
{
switch(dir)
{
case 1://up
rotate(jz,3);
mergeLeft(jz);
rotate(jz,1);
break;
case 2://down
rotate(jz,1);
mergeLeft(jz);
rotate(jz,3);
break;
case 3://left
mergeLeft(jz);
break;
case 4://right
rotate(jz,2);
mergeLeft(jz);
rotate(jz,2);
break;
default:
break;
}
}
我的代码
#include <iostream>
using namespace std;
#define N 4
void mergeLeft(int jz[][N])
{
for(int l = 0;l<N;l++) //循环处理每一行
{
int *line = jz[l];
int j,k;
for(int i=0;i<N;i++)
{
j = i;
while(!line[j] && j<N) //从当前位置开始找到第一个不为空的位置
j++;
k=j+1;
while(!line[k] && k<N) //找到下一个不为空的数字
k++;
if(k<N && line[j] == line[k]) //判断两个数能否合并
{
line[j] = 0;
line[i] = 2 * line[k]; // i和j有可能相等,所以使用k
line[k] = 0;
}
else if(j<N) //移动
{
int tmp = line[j]; //i和j可能相等
line[j] = 0;
line[i] = tmp;
}
else
{
break;//右边已经没有数字了
}
}
}
}
void rotate(int jz[][N],int times)
{
int tmp[N][N];
memcpy(tmp,jz,sizeof(tmp));
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
jz[i][j] = tmp[N-1-j][i];
}
}
if(times>1)
rotate(jz,times-1);
}
void merge(int jz[][N],int dir)
{
switch(dir)
{
case 1://up
rotate(jz,3);
mergeLeft(jz);
rotate(jz,1);
break;
case 2://down
rotate(jz,1);
mergeLeft(jz);
rotate(jz,3);
break;
case 3://left
mergeLeft(jz);
break;
case 4://right
rotate(jz,2);
mergeLeft(jz);
rotate(jz,2);
break;
default:
break;
}
}
int main()
{
int jz[N][N]={{2,0,4,8},
{0,2,0,2},
{0,0,2,0},
{2,0,2,8}};
int move = 3;
cin >> move;
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
cin >> jz[i][j];
}
}
merge(jz,move);
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
cout << jz[i][j]<<" ";
}
cout <<endl;
}
}
总结
如果追求速度的话,还是应该每个方向的合并函数都写一遍。不过在4*4这种超小规模来说,旋转矩阵耗时可以忽略不计,而且这样的代码更易于理解和维护,所以不应该浪费时间把4个方向的旋转函数都实现出来。