2016阿里校招算法岗笔试题——魔方

时间:2023-01-31 13:40:55

笔试被虐惨,选择题一半多的概率题,每道题差不多3、5分钟,so,最后好几个没有算。

附加两个大题,一个是编写魔方,另一个是机器学习的分布不均衡问题和SVM。魔方题没有写完,机器学习的题目也只答了一问...

当天晚上把魔方的代码完善了下,没有找到好的解法,硬写的,麻烦的要死。

晚上的时候网上已经有题目了,详情如下,最后结果的输出其实不一定要与题目一致,因为每一面的方向没有明确定义,颜色对了即可,比如本文的程序里,Y面与题目的Y面相差180度:

 一个三阶魔方由六个面组成,颜色分别是白色(W)、对面为黄色(Y)、红色(R)、对面为橙色(O)、绿色(G)、对面为蓝色(B)。如果手持魔方,白色向上,红色朝向自己,则绿色在左手侧。
请写一个程序,对一个处于还原状态(各面各块同色)的魔方,进行操作,打印操作后的魔方状态。操作指令为单个操作指令组成的字符串。单个操作指令包括:
1)U:白色面顺时针旋转90度
2)D:黄色面顺时针旋转90度
3)F:红色面顺时针旋转90度
4)B:橙色面顺时针旋转90度
5)L:绿色面顺时针旋转90度
6)R:蓝色面顺时针旋转90度
其中顺时针旋转定义为将该面朝向自己时的顺时针方向。
按WYROGB的顺序输出经过操作后6个面的状态。每个面首先输出中心块颜色,然后从此面面向自己时的左下角块开始,顺时针顺出每块的颜色。输出一面后换行。
请设计一个数据结构表示魔方,并基于该数据结构完成功能。
请用C/C++,Java或Python语言实现。请注意尽量以可成功编译或可直接运行为标准来编写代码。
 
示例:
输入:
LR
输出:
WOOOWRRRW
YRRRYOOOY
RWWWRYYYR
OYYYOWWWO
GGGGGGGGG
BBBBBBBBB


代码如下:

#include <stdio.h>
#include <stdlib.h>

struct panel {
    //暴力解法,每个面的面板颜色和四个接触的边
    int a[3][3];    //每个面的颜色
    char status;    //本面的颜色符号
    struct panel *u;    //四个相邻的面
    int ul;     //相邻的边是相应面的那一条1-6表示1-3行+1-3列,-号表示逆序
    struct panel *r;
    int rl;
    struct panel *d;
    int dl;
    struct panel *l;
    int ll;
    };

typedef struct panel PP;

void initPanel(PP *p, int color);
void changePanel(PP *p, PP *u, PP *r, PP *d, PP *l);
void rot(PP *p);
void getLine(PP *p,int line,int b[]);
void setLine(PP *p,int line ,int b[]);
int abValue(int x);
void printPanel(PP *p, int check);
char intTochar(int i);
void printA(int a[]);

int main(int argc, char **argv)
{
    char c;
    
	PP Wp;
    PP Op;
    PP Bp;
    PP Rp;
    PP Gp;
    PP Yp;
    
    //定义每个面的颜色
    initPanel(&Wp,1);
    initPanel(&Op,2);
    initPanel(&Bp,3);
    initPanel(&Rp,4);
    initPanel(&Gp,5);
    initPanel(&Yp,6);
    
    /*给白色面定义四个临边,尼玛太麻烦了,有好一点的算法没?
    以这个方向定义,输出也是用的这个方向
    
         ---
        | O |
     --- --- --- ---
    | G | W | B | Y |
     --- --- --- ---
        | R |
         ---
    */
    Wp.status = 'W';
    Wp.u = &Op;
    Wp.ul = 3;
    Wp.r = &Bp;
    Wp.rl = 4;
    Wp.d = &Rp;
    Wp.dl = 1;
    Wp.l = &Gp;
    Wp.ll = 6;
    //橘色定义
    Op.status = 'O';
    Op.u = &Yp;
    Op.ul = -1;
    Op.r = &Bp;
    Op.rl = -1;
    Op.d = &Wp;
    Op.dl = 1;
    Op.l = &Gp;
    Op.ll = 1;
    //蓝色定义
    Bp.status = 'B';
    Bp.u = &Op;
    Bp.ul = -6;
    Bp.r = &Yp;
    Bp.rl = 4;
    Bp.d = &Rp;
    Bp.dl = 6;
    Bp.l = &Wp;
    Bp.ll = 6;
    //红色定义
    Rp.status = 'R';
    Rp.u = &Wp;
    Rp.ul = 3;
    Rp.r = &Bp;
    Rp.rl = 3;
    Rp.d =&Yp;
    Rp.dl = -3;
    Rp.l = &Gp;
    Rp.ll = -3;
    //绿色定义
    Gp.status = 'G';
    Gp.u = &Op;
    Gp.ul = 4;
    Gp.r = &Wp;
    Gp.rl = 4;
    Gp.d = &Rp;
    Gp.dl = -4;
    Gp.l = &Yp;
    Gp.ll = 6;
    //黄色定义
    Yp.status = 'Y';
    Yp.u = &Op;
    Yp.ul = -1;
    Yp.r = &Gp;
    Yp.rl = 4;
    Yp.d = &Rp;
    Yp.dl = -3;
    Yp.l = &Bp;
    Yp.ll = 6;
    
    while((c = getchar()) != '\n'){
        if(c == 'U')
            changePanel(&Wp, Wp.u, Wp.r, Wp.d, Wp.l);   //改变本面和相邻的四个面
        if(c == 'D')
            changePanel(&Yp, Yp.u, Yp.r, Yp.d, Yp.l);
        if(c == 'F')
            changePanel(&Rp, Rp.u, Rp.r, Rp.d, Rp.l);
        if(c == 'B')
            changePanel(&Op, Op.u, Op.r, Op.d, Op.l);
        if(c == 'L')
            changePanel(&Gp, Gp.u, Gp.r, Gp.d, Gp.l);
        if(c == 'R')
            changePanel(&Bp, Bp.u, Bp.r, Bp.d, Bp.l);
    }
    
    //输出每一个面,注意输出的顺序,1代表与定义顺序一致,0代表翻转180
    printPanel(&Wp, 1);
    printPanel(&Yp, 0);
    printPanel(&Rp, 1);
    printPanel(&Op, 1);
    printPanel(&Gp, 1);
    printPanel(&Bp, 1);
    
	return 0;
}

//初始每个面的颜色
void initPanel(PP *p, int color){
    int i,j;
    
    for(i = 0;i < 3;i++)
        for(j = 0;j < 3;j++)
        p->a[i][j] = color;
    
        
    }

//旋转
void changePanel(PP *p, PP *u, PP *r, PP *d, PP *l){
    
    //旋转本面二维数组
    rot(p);
    
    //旋转四个边
    int ua[3],ra[3],da[3],la[3];
    int i,j;
    getLine(p->u,p->ul,ua);
    getLine(p->r,p->rl,ra);
    getLine(p->d,p->dl,da);
    getLine(p->l,p->ll,la);
    
    //输出本面的当前四个边
    printf("%c %dth is\n",p->u->status,p->ul);
    printA(ua);
    printf("%c %dth is\n",p->r->status,p->rl);
    printA(ra);
    printf("%c %dth is\n",p->d->status,p->dl);
    printA(da);
    printf("%c %dth is\n",p->l->status,p->ll);
    printA(la);
    printf("\n");
    
    //转换4条边,由于下边和左边的定义顺序是左到右、上到下排列,与顺时针方向相反,在转换时要注意
    for(i = 0;i < 3;i++){
        j = ua[i];
        ua[i] = la[2-i];
        la[2-i] = da[2-i];
        da[2-i] = ra[i];
        ra[i] = j;
        }
    
    //输出旋转后本面的当前四个边
    printf("%c %d th is\n",p->u->status,p->ul);
    printA(ua);
    printf("%c %d th is\n",p->r->status,p->rl);
    printA(ra);
    printf("%c %d th is\n",p->d->status,p->dl);
    printA(da);
    printf("%c %d th is\n",p->l->status,p->ll);
    printA(la);
    printf("\n");
    
    //将旋转后的4个边赋值给相应的临面 
    setLine(p->u,p->ul,ua);
    setLine(p->r,p->rl,ra);
    setLine(p->d,p->dl,da);
    setLine(p->l,p->ll,la);
    
    }

//旋转二维数组,注意每一行交换两个
void rot(PP *p){
    int i,j;
    for(i = 0; i < 2; i++){
        j = p->a[0][i];
        p->a[0][i] = p->a[2-i][0];
        p->a[2-i][0] = p->a[2][2-i];
        p->a[2][2-i] = p->a[i][2];
        p->a[i][2] = j;
        }
    }

//获取面的某条边数据
void getLine(PP *p,int line,int b[]){
    int i;
    
    if(abValue(line) < 4){
        for(i = 0;i < 3;i++){
            if(line < 0)
                b[i] = p->a[abValue(line) - 1][2-i];
            if(line > 0)
                b[i] = p->a[line - 1][i];
            }
        }
    else if(abValue(line) >= 4){
        for(i = 0;i < 3;i++){
            if(line < 0)
                b[i] = p->a[2-i][abValue(line) - 4];
            if(line > 0)
                b[i] = p->a[i][line - 4];
            }
        }
    }

//赋值面的某条边
void setLine(PP *p,int line ,int b[]){
    int i;
    
    if(abValue(line) < 4){
        for(i = 0;i < 3;i++){
            if(line < 0)
                p->a[abValue(line) - 1][2-i] = b[i];
            if(line > 0)
                p->a[line - 1][i] = b[i];
            }
        }
    else if(abValue(line) >= 4){
        for(i = 0;i < 3;i++){
            if(line < 0)
                p->a[2-i][abValue(line) - 4] = b[i];
            if(line > 0)
                p->a[i][line - 4] = b[i];
            }
        }
    }

//取绝对值
int abValue(int x){
    if(x < 0)
        return 0-x;
    else
        return x;
    }

//check为定义如何输出,定义每一面时是按照正方体展开的方向,最下面的Y貌似与定义顺序不同
void printPanel(PP *p, int check){
    int i,j,k;
    if(check == 1){
        //正序,j+k*正序下标,无变化
        j = 0;
        k = 1;
    }
    else if(check == 0){
        //逆序下标=2-正序下标 j+k*正序下标
        j = 2;
        k = -1;
        }
//输出二维数组,先输出中心点,再输出4个边,左下开始顺时针
printf("%c",intTochar(p->a[1][1]));
for(i = 0;i < 2;i++)
    printf("%c",intTochar(p->a[j+k*(2-i)][j+k*0]));
for(i = 0;i < 2;i++)
    printf("%c",intTochar(p->a[j+k*0][j+k*i]));
for(i = 0;i < 2;i++)
    printf("%c",intTochar(p->a[j+k*i][j+k*2]));
for(i = 0;i < 2;i++)
    printf("%c",intTochar(p->a[j+k*2][j+k*(2-i)]));
printf("\n");
    }

//由数字颜色转为字符
char intTochar(int i){
    
    switch(i){
        case 1:
          return 'W';
          break;
        case 2:
          return 'O';
          break;
        case 3:
          return 'B';
          break;
        case 4:
          return 'R';
          break;
        case 5:
          return 'G';
          break;
        case 6:
          return 'Y';
          break;
        default:
          return 'N';
          break;
        }
        
    }

//输出暂存数组
void printA(int a[]){
    printf("%c",intTochar(a[0]));
    printf("%c",intTochar(a[1]));
    printf("%c",intTochar(a[2]));
    printf("\n");
    }