华为OD机试记录

上周参加了华为OD的机试,两个半小时三道题目,现进行复盘和记录。

第一题:

给你一个m*n(0< m,n < 1024,)的数组array,成员初始全为0,给你两个坐标{x1,y2},{x2,y2},且使得array[x1][y1] = 1,array[x2][y2] = 1,数组成员为1的成员具有扩散性,也即是,每经过1秒,坐标{x1,y2},{x2,y2}的上、下、左、右四个方向的数组成员都会被扩散为1.
**请问,经过多少秒后数组array的成员全部为1 **

示例图:
在这里插入图片描述

​ 刚读完题我就感觉到没有很简单的样子,思考一会儿后,我想,数组题目一般都会涉及到边界判断,数组成员扩散,那么上下左右,就分别对应{x-1,y}、{x+1,y}、{x,y-1}、{x,y+1}这四种情况。

于是我就想,每经历一秒,当前值为1的两成员{x1,y2},{x2,y2},就会扩散{x1-1,y1}、{x1+1,y1}、{x1,y1-1}、{x1,y1+1}与{x2-1,y2}、{x2+1,y2}、{x2,y2-1}、{x2,y2+1}这八个点,这就是扩散过程。

又由于,这八个点并不一定都在数组范围内,所以需要加上边界判断。其次,有可能 {x1-1,y1}、{x1+1,y1}、{x1,y1-1}、{x1,y1+1}与{x2-1,y2}、{x2+1,y2}、{x2,y2-1}、{x2,y2+1}存在重复元素,要进行重复性判断,即本次扩散过程中,前面的被扩散过的,后面的就不用再扩散了。

再然后,在本轮扩散过程结束后,会有两种情况,当前的数组已经全被扩散了,这个时候满足条件,不用再进行下一轮的扩散了。另一种情况,当前数组较大,本轮扩散结束后还有未扩散的成员,那么就要再次进行上述相同的扩散过程,所以这里肯定是用递归。

想好了就写。下面这是我的当时提交的代码,这次机试是可以自己调试的,我自己调试能够输出正确结果,但是会有内存方面的问题。

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

void free_2_arrary(int **g,int size)
{
    int  i = 0;
    for (i = 0;i < size;i++)
    {
        if (g[i])
        {
            free(g[i]);
            g[i] = NULL;    
        }
    }

    if (g)
    {
        free(g);
        g = NULL;
    }
}

//使用数组形式来试试
void test(int **arrary,int m,int n,int **this_arrary,int this_size,int *num,int *second)
{
    int i = 0;
    int **new_array = NULL;
    int new_size = 0;
    int next = 0;

    for (i = 0; i < this_size;i++)
    {
        //(x+1,y)
        if (this_arrary[i][0]+1 < m && arrary[this_arrary[i][0]+1][this_arrary[i][1]] == 0)
        {
            arrary[this_arrary[i][0]+1][this_arrary[i][1]] = 1;

            new_array = realloc(new_array,sizeof(int *)*(new_size+1));
            new_array[new_size] = malloc(sizeof(int)*2);
            new_array[new_size][0] = this_arrary[i][0]+1;
            new_array[new_size][1] = this_arrary[i][1];

            new_size += 1;
            *num += 1;
            next = 1;
        }

        //(x-1,y)
        if (this_arrary[i][0] - 1 >=0 && arrary[this_arrary[i][0] - 1][this_arrary[i][1]] == 0)
        {
            arrary[this_arrary[i][0] - 1][this_arrary[i][1]] = 1;

            new_array = realloc(new_array,sizeof(int *)*(new_size+1));
            new_array[new_size] = malloc(sizeof(int)*2);
            new_array[new_size][0] = this_arrary[i][0] - 1;
            new_array[new_size][1] = this_arrary[i][1];

            new_size += 1;
            *num += 1;
            next = 1;
        }

        //(x,y+1)
        if (this_arrary[i][1]+1 < n && arrary[this_arrary[i][0]][this_arrary[i][1]+1] == 0)
        {
            arrary[this_arrary[i][0]][this_arrary[i][1]+1] = 1;

            new_array = realloc(new_array,sizeof(int *)*(new_size+1));
            new_array[new_size] = malloc(sizeof(int)*2);
            new_array[new_size][0] = this_arrary[i][0];
            new_array[new_size][1] = this_arrary[i][1]+1;

            new_size += 1;
            *num += 1;
            next = 1;
        }

        //(x,y-1)
        if (this_arrary[i][1]-1 >=0 && arrary[this_arrary[i][0]][this_arrary[i][1]-1] == 0)
        {
            arrary[this_arrary[i][0]][this_arrary[i][1]-1] = 1;

            new_array = realloc(new_array,sizeof(int *)*(new_size+1));
            new_array[new_size] = malloc(sizeof(int)*2);
            new_array[new_size][0] = this_arrary[i][0];
            new_array[new_size][1] = this_arrary[i][1]-1;

            new_size += 1;
            *num += 1;
            next = 1;
        }
    }

	if (next == 1)//本轮遍历是否进行
	{
		(*second) += 1;
	}
	
    free_2_arrary(this_arrary,this_size);

    if (*num == m*n)
    {
        if (new_size > 0)
        {
            free_2_arrary(new_array,new_size);
        }

        return;
    }

    test(arrary,m,n,new_array,new_size,num,second);
}

int main()
{
    int m,n;
    int **arrary = NULL;
    int i = 0;
    int j = 0;
    int num = 0;
    int p1_x,p1_y;
    int p2_x,p2_y;
    int second = 0;
    int **this_array = NULL;
    int this_size = 0;

    while (EOF != scanf("%d%d%d%d%d%d",&m,&n,&p1_x,&p1_y,&p2_x,&p2_y))
    {
        second = 0;
        arrary = (int **)malloc(sizeof(int *)*m);
        for (i = 0;i < m;i++)
        {
            arrary[i] = (int *)malloc(sizeof(int)*n);
            memset(arrary[i],0,sizeof(int)*n);
        }

        arrary[p1_x][p1_y] = 1;
        arrary[p2_x][p2_y] = 1;


        this_size = 2;
        this_array = (int **)malloc(sizeof(int *)*2);
        this_array[0] = (int *)malloc(sizeof(int)*2);
        this_array[1] = (int *)malloc(sizeof(int)*2);

        this_array[0][0] = p1_x;
        this_array[0][1] = p1_y;
        this_array[1][0] = p2_x;
        this_array[1][1] = p2_y;

        num += 2;
        test(arrary,m,n,this_array,this_size,&num,&second);

        free_2_arrary(arrary,m);
        printf("%d\n",second);
    }

    return 0;
}

当时写出这道题花了几乎一个半小时,写完以后调试是有内存方面的问题的,当时很着急,感觉核心的代码没什么问题了,一直单步调试也没搞明白哪里有问题。

我首先讲解一下我的核心逻辑:

思路如下:

​ 扩散过程中,通过边界判断和成员判断,得到新的扩散成员集合,又把新的扩散成员集合传递到扩散过程函数继续判断,直到扩散数目达到m*n,也即是总的数组成员个数。

基于以上思路,所以我的扩散过程函数是这样设计的。

扩散过程函数的设计

void test(int **arrary,int m,int n,int **this_array,int this_size,int *num,int *second)

第一个参数,一个int型的二级指针,是为了向函数传入一个m*n的二维数组。参数m,n代表二维数组的行和列数。
第四个参数,是一个被扩散坐标集合,也即是一个二维数组,this_size表明这个集合数组中的元素个数。
第一次调用时,this_array由外部动态分配和赋值,值如下所示:

this_size = 2;
this_array = (int **)malloc(sizeof(int *)*2);
this_array[0] = (int *)malloc(sizeof(int)*2);
this_array[1] = (int *)malloc(sizeof(int)*2);

this_array[0][0] = p1_x;
this_array[0][1] = p1_y;
this_array[1][0] = p2_x;
this_array[1][1] = p2_y;

第六个参数,代表被扩散个数的计数器,所以使用一级指针形式,方便在递归过程中改变它的值。
第七个参数,代表消耗的时间的计数器,使用指针同样是为了传递和修改。

扩散过程

代码如下

for (i = 0; i < this_size;i++)
{
    //(x+1,y)
    if (this_array[i][0]+1 < m && arrary[this_array[i][0]+1][this_array[i][1]] == 0)
    {
        arrary[this_array[i][0]+1][this_array[i][1]] = 1;

        new_array = realloc(new_array,sizeof(int *)*(new_size+1));
        new_array[new_size] = malloc(sizeof(int)*2);
        new_array[new_size][0] = this_array[i][0]+1;
        new_array[new_size][1] = this_array[i][1];

        new_size += 1;
        *num += 1;
        next = 1;
    }

    //(x-1,y)
    if (this_array[i][0] - 1 >=0 && arrary[this_array[i][0] - 1][this_array[i][1]] == 0)
    {
        arrary[this_array[i][0] - 1][this_array[i][1]] = 1;

        new_array = realloc(new_array,sizeof(int *)*(new_size+1));
        new_array[new_size] = malloc(sizeof(int)*2);
        new_array[new_size][0] = this_array[i][0] - 1;
        new_array[new_size][1] = this_array[i][1];

        new_size += 1;
        *num += 1;
        next = 1;
    }

    //(x,y+1)
    if (this_array[i][1]+1 < n && arrary[this_array[i][0]][this_array[i][1]+1] == 0)
    {
        arrary[this_array[i][0]][this_array[i][1]+1] = 1;

        new_array = realloc(new_array,sizeof(int *)*(new_size+1));
        new_array[new_size] = malloc(sizeof(int)*2);
        new_array[new_size][0] = this_array[i][0];
        new_array[new_size][1] = this_array[i][1]+1;

        new_size += 1;
        *num += 1;
        next = 1;
    }

    //(x,y-1)
    if (this_array[i][1]-1 >=0 && arrary[this_array[i][0]][this_array[i][1]-1] == 0)
    {
        arrary[this_array[i][0]][this_array[i][1]-1] = 1;

        new_array = realloc(new_array,sizeof(int *)*(new_size+1));
        new_array[new_size] = malloc(sizeof(int)*2);
        new_array[new_size][0] = this_array[i][0];
        new_array[new_size][1] = this_array[i][1]-1;

        new_size += 1;
        *num += 1;
        next = 1;
    }
}

由于我们的当前扩散过程中,是所有的节点都在进行扩散,所以我们必须使用for循环,遍历当前已经被扩散了的所有点,这里一定要理解。从某个被扩散的点,{p_x1,p_y1}开始,我们判断{p_x1+1,y}这个点是否满足要求,即p_x1+1 < m && arrary[p_x1+1][p_y1]没被扩散(==0),注意这个与条件的先后关系,即,是先满足在数组范围内,再判断是否能已经被扩散过了。当这个新点从0变为1,那么就是一个新点,所以我们用于传递给下一轮过程的二维数组new_array的成员要++,这里为了动态扩增使用realloc,即:

arrary[this_array[i][0]+1][this_array[i][1]] = 1;//表明被感染
new_array = realloc(new_array,sizeof(int *)*(new_size+1));//新被感染集合的空间扩增1个
new_array[new_size] = malloc(sizeof(int)*2);//分配内存
new_array[new_size][0] = this_array[i][0]+1;//赋值x
new_array[new_size][1] = this_array[i][1];//赋值y
new_size += 1;//新被感染集合中成员数++
*num += 1;//被感染成员个数++

这样,直到本轮的被扩散集合都被遍历一遍后,那么就消耗了1s,所以:

if (next == 1)//本轮遍历是否进行
{
	(*second) += 1;
}
	

然后判断是否已经扩散完,是的话函数就该返回了,否则就进入下一轮扩散过程。

if (*num == m*n)//数组已经被已经感染完全
{
    if (new_size > 0)
    {
        free_2_arrary(new_array,new_size);
    }

    return;
}

test(arrary,m,n,new_array,new_size,num,second);//进入下一轮感染过程

代码讲解到这里,逻辑上来说是完全没问题的,但是点击保存调式,有内存方面的问题,case一个都没通过,当时时间已经所剩不多了,我很着急啊,一直在gdb单步调试,感觉没啥问题啊。

后来只剩几分钟,感觉调不过了我就做下一题,下一题一读题就知道很简单,需要有一个排序过程,我就用冒泡排序,刚写完冒泡排序,和主函数输入输出步骤,时间就到了然后就交卷了。

今天我自己继续调试上述过程,还是找出了上述代码的问题所在

while (EOF != scanf("%d%d%d%d%d%d",&m,&n,&p1_x,&p1_y,&p2_x,&p2_y))
{
    second = 0;
    arrary = (int **)malloc(sizeof(int *)*m);
    for (i = 0;i < m;i++)
    {
        arrary[i] = (int *)malloc(sizeof(int)*n);
        memset(arrary[i],0,sizeof(int)*n);
    }

    arrary[p1_x][p1_y] = 1;
    arrary[p2_x][p2_y] = 1;


    this_size = 2;
    this_array = (int **)malloc(sizeof(int *)*2);
    this_array[0] = (int *)malloc(sizeof(int)*2);
    this_array[1] = (int *)malloc(sizeof(int)*2);

    this_array[0][0] = p1_x;
    this_array[0][1] = p1_y;
    this_array[1][0] = p2_x;
    this_array[1][1] = p2_y;

    num += 2;
    test(arrary,m,n,this_array,this_size,&num,&second);

    free_2_arrary(arrary,m);
    //free_2_arrary(this_array,this_size);

    printf("%d",second);
}

这个主函数处理输入输出的,这个num在while循环中,每次都应该重置为2,所以num+=2导致错误,应该写为num=2。当时一门心思去调test这个函数去了,却没有考虑是不是输入输出设计的合理与否,导致题目没有通过。

修改后的完整答案如下:

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

void free_2_arrary(int **g,int size)
{
    int  i = 0;
    for (i = 0;i < size;i++)
    {
        if (g[i])
        {
            free(g[i]);
            g[i] = NULL;    
        }
    }
    
    if (g)
    {
        free(g);
		g = NULL;
    }
}

//使用数组形式来试试
void test(int **arrary,int m,int n,int **this_arrary,int this_size,int *num,int *second)
{
    int i = 0;
    int **new_array = NULL;
    int new_size = 0;
    int next = 0;
	
    for (i = 0; i < this_size;i++)
    {
        //(x+1,y)
        if (this_arrary[i][0]+1 < m && arrary[this_arrary[i][0]+1][this_arrary[i][1]] == 0)
        {
            arrary[this_arrary[i][0]+1][this_arrary[i][1]] = 1;
            
            new_array = realloc(new_array,sizeof(int *)*(new_size+1));
            new_array[new_size] = malloc(sizeof(int)*2);
            new_array[new_size][0] = this_arrary[i][0]+1;
            new_array[new_size][1] = this_arrary[i][1];
            
            new_size += 1;
            *num += 1;
            next = 1;
        }

        //(x-1,y)
        if (this_arrary[i][0] - 1 >=0 && arrary[this_arrary[i][0] - 1][this_arrary[i][1]] == 0)
        {
            arrary[this_arrary[i][0] - 1][this_arrary[i][1]] = 1;
            
            new_array = realloc(new_array,sizeof(int *)*(new_size+1));
            new_array[new_size] = malloc(sizeof(int)*2);
            new_array[new_size][0] = this_arrary[i][0] - 1;
            new_array[new_size][1] = this_arrary[i][1];
            
            new_size += 1;
            *num += 1;
            next = 1;
        }

        //(x,y+1)
        if (this_arrary[i][1]+1 < n && arrary[this_arrary[i][0]][this_arrary[i][1]+1] == 0)
        {
            arrary[this_arrary[i][0]][this_arrary[i][1]+1] = 1;
            
            new_array = realloc(new_array,sizeof(int *)*(new_size+1));
            new_array[new_size] = malloc(sizeof(int)*2);
            new_array[new_size][0] = this_arrary[i][0];
            new_array[new_size][1] = this_arrary[i][1]+1;
			
            new_size += 1;
            *num += 1;
            next = 1;
        }

        //(x,y-1)
        if (this_arrary[i][1]-1 >=0 && arrary[this_arrary[i][0]][this_arrary[i][1]-1] == 0)
        {
            arrary[this_arrary[i][0]][this_arrary[i][1]-1] = 1;
            
            new_array = realloc(new_array,sizeof(int *)*(new_size+1));
            new_array[new_size] = malloc(sizeof(int)*2);
            new_array[new_size][0] = this_arrary[i][0];
            new_array[new_size][1] = this_arrary[i][1]-1;
			
            new_size += 1;
            *num += 1;
            next = 1;
        }
    }
	
	if (next == 1)//本轮遍历是否进行
	{
		(*second) += 1;
	}
	
	free_2_arrary(this_arrary,this_size);
	
	if (*num == m*n)
    {
		if (new_size > 0)
		{
			free_2_arrary(new_array,new_size);
		}
		
        return;
    }
	
    test(arrary,m,n,new_array,new_size,num,second,1);
}

int main()
{
    int m,n;
    int **arrary = NULL;
    int i = 0;
    int j = 0;
	
    int num = 0;
    int p1_x,p1_y;
    int p2_x,p2_y;
    int second = 0;
	int **this_array = NULL;
	int this_size = 0;
    
    while (EOF != scanf("%d%d%d%d%d%d",&m,&n,&p1_x,&p1_y,&p2_x,&p2_y))
    {
        second = 0;
        arrary = (int **)malloc(sizeof(int *)*m);
        for (i = 0;i < m;i++)
        {
            arrary[i] = (int *)malloc(sizeof(int)*n);
            memset(arrary[i],0,sizeof(int)*n);
        }
        
        arrary[p1_x][p1_y] = 1;
        arrary[p2_x][p2_y] = 1;
        
        
        this_size = 2;
        this_array = (int **)malloc(sizeof(int *)*2);
        this_array[0] = (int *)malloc(sizeof(int)*2);
        this_array[1] = (int *)malloc(sizeof(int)*2);
        
        this_array[0][0] = p1_x;
        this_array[0][1] = p1_y;
        this_array[1][0] = p2_x;
        this_array[1][1] = p2_y;
        
		num = 2;
        test(arrary,m,n,this_array,this_size,&num,&second,0);
		free_2_arrary(arrary,m);
		
        printf("%d",second);
	}
	
	return 0;
}

借助队列来做

这种类似迷宫的题,可以借助队列来进行层次遍历,之前的做法涉及了大量的一二级指针的使用,很容易出现问题。这个题限制数的范围为0~1024,而队列是先入先出,如果使用顺序队列:
(1) 如果数组m=1024,n=1024,队列头指针和尾指针一进一出,那么是有可能出现队列头指针指向1024*1024的位置的,所以我们必须使用循环队列。
(2) 在数组m=1,n=1024时,队列头指针和尾指针一进一出,那么最大的偏移不会超过1024*2,所以我们设置队列大小为2048。
此时,综合(1)、(2),我们就使用一个循环队列,其大小为2048.代码如下:

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

typedef struct st_g_queue
{
	int pos_set[2048][2];//用队列的话需要很好地设计存储坐标集合的空间避免溢出
	int top;
	int tail;
	int size;
}queue;


void free_2_arrary(int **g,int size)
{
    int  i = 0;
    for (i = 0;i < size;i++)
    {
        if (g[i])
        {
            free(g[i]);
            g[i] = NULL;
        }
    }
    
    if (g)
    {
        free(g);
		g = NULL;//没用
    }
}

int main()
{
    int m,n;
    int **arrary = NULL;
    int i = 0;
    int j = 0;
	
    int num = 0;
    int p1_x,p1_y;
    int p2_x,p2_y;
    int second = 0;
	int times = 0;
	int next = 0;
	
	queue g_queue;
    while (EOF != scanf("%d%d%d%d%d%d",&m,&n,&p1_x,&p1_y,&p2_x,&p2_y))
    {
		//队列初始化
		g_queue.top = 0;
		g_queue.tail = 0;
		g_queue.size = 0;
		
		times = 0;
		second = 0;
		
		arrary = NULL;
        arrary = (int **)malloc(sizeof(int *)*m);
        for (i = 0;i < m;i++)
        {
            arrary[i] = (int *)malloc(sizeof(int)*n);
            memset(arrary[i],0,sizeof(int)*n);
        }
		
        arrary[p1_x][p1_y] = 1;
        arrary[p2_x][p2_y] = 1;
		
		g_queue.pos_set[g_queue.top][0] = p1_x;//x
		g_queue.pos_set[g_queue.top][1] = p1_y;//y
		
		g_queue.top++;
		g_queue.top %= 2048;
		g_queue.size++;
		
		g_queue.pos_set[g_queue.top][0] = p2_x;//x
		g_queue.pos_set[g_queue.top][1] = p2_y;//y
		
		g_queue.top++;
		g_queue.top %= 2048;
		g_queue.size++;
		
		num = 2;		
		//开始层次遍历
		while (g_queue.size > 0 && num < m*n)
		{
			int tmpsize = g_queue.size;
			
			for (int i = 0;i < tmpsize;i++)
			{
				int x = g_queue.pos_set[g_queue.tail][0];
				int y = g_queue.pos_set[g_queue.tail][1];
				
				g_queue.tail += 1;
				g_queue.tail %= 2048;
				
				g_queue.size -= 1;
				
				//(x+1,y)
				if (x+1 < m && arrary[x+1][y] == 0)
				{
					arrary[x+1][y] = 1;
					g_queue.pos_set[g_queue.top][0] = x+1;//x
					g_queue.pos_set[g_queue.top][1] = y;//y
					g_queue.top++;
					g_queue.size++;
					
					g_queue.top %= 2048;
					
					num++;
				}
				
				//(x-1,y)
				if (x-1 >=0 && arrary[x-1][y] == 0)
				{
					arrary[x - 1][y] = 1;
					g_queue.pos_set[g_queue.top][0] = x-1;//x
					g_queue.pos_set[g_queue.top][1] = y;//y
					g_queue.top++;
					g_queue.top %= 2048;
					g_queue.size++;
					
					num++;
				}

				//(x,y+1)
				if (y+1 < n && arrary[x][y+1] == 0)
				{
					arrary[x][y+1] = 1;
					g_queue.pos_set[g_queue.top][0] = x;//x
					g_queue.pos_set[g_queue.top][1] = y+1;//y
					g_queue.top++;
					g_queue.top %= 2048;
					g_queue.size++;
					
					num++;
				}
				
				//(x,y-1)
				if (y-1 >=0 && arrary[x][y-1] == 0)
				{
					arrary[x][y-1] = 1;
					g_queue.pos_set[g_queue.top][0] = x;//x
					g_queue.pos_set[g_queue.top][1] = y-1;//y
					g_queue.top++;
					g_queue.top %= 2048;
					g_queue.size++;
					
					num++;
				}
			}
			
			second += 1;
			
			if (num == m*n)
			{
				break;
			}
		}
        printf("%d\n",second);	
		free_2_arrary(arrary,m);
	}	
	return 0;
}

第二题

第二题很简单,但是没有时间做了。

题目:一辆货车载重N,一批快递重量为{a,b,c,d,e…},求卡车能装的最多快递数?

这题很简单,将快递从小到大排序,然后从第一个判断和累加,若超出N就跳出。代码就不上了。

第三题没机会见到了,本次机试如上。

总结

1.要熟悉牛客的输入输出
2.先读题找简单的做
3.调试每个函数都要考虑一下。

  • 7
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 28
    评论
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值