蓝桥杯真题 整理玩具【第九届】【决赛】【B组】(C语言实现)

资源限制

时间限制:1.0s   内存限制:256.0MB

  小明有一套玩具,一共包含NxM个部件。这些部件摆放在一个包含NxM个小格子的玩具盒中,每个小格子中恰好摆放一个部件。

  每一个部件上标记有一个0~9的整数,有可能有多个部件标记相同的整数。

  小明对玩具的摆放有特殊的要求:标记相同整数的部件必须摆在一起,组成一个矩形形状。

  如以下摆放是满足要求的:

  00022
  00033
  44444

  12244
  12244
  12233

  01234
  56789

  以下摆放不满足要求:

  11122
  11122
  33311

  111111
  122221
  122221
  111111

  11122
  11113
  33333

  给出一种摆放方式,请你判断是否符合小明的要求。

输入格式

  ----
  输入包含多组数据。
  第一行包含一个整数T,代表数据组数。 (1 <= T <= 10)
  以下包含T组数据。
  每组数据第一行包含两个整数N和M。 (1 <= N, M <= 10)
  以下包含N行M列的矩阵,代表摆放方式。

输出格式

  ---
  对于每组数据,输出YES或者NO代表是否符合小明的要求。

样例输入

3
3 5
00022
00033
44444
3 5
11122
11122
33311
2 5
01234
56789

样例输出

YES
NO
YES

先说思路:

题目给的限定条件是零件必须堆放成矩形而且只能堆放在一个位置,所以我们就从这两方面出发。

矩形:怎样判断是否是矩形呢,我们可以先找一个元素,然后再他所在的行和列找和他相同且连续的元素,记录一下个数,这样一个矩形的长和宽就被记录下来了。然后我们以这个矩形面积为范围,遍历里面的元素,有不相同的,就返回否,输出“NO”。

图示:

111113
111113
111113
111113
111113
002223
002223
002223

如果我们先从第一行第一列开始,从当前行和当前列分别计数与1相同的个数,就能很轻易的记录出编号为1的零件堆放的矩形的长和宽等于5和5。

接下来就是两种特殊的情况,一个是在矩形覆盖的面积内,可能会出现其他零件;另一个是在计数的矩形面积外面,可能会出现与矩形内相同的零件。

首先解决矩形内的问题,数据量比较小,直接将我们已经求出来的长和宽作为范围,在这个矩形内对元素进行遍历,看看是否都一样的,如果不一样,直接返回否输出“NO”就行了;

接着解决矩形外的问题,这个问题我们稍后讲解。

首先着手数据的存储,我们使用一个结构体,里面包含零件编号以及一个标记

typedef struct data
{
	int num;//编号
	int bj;//标记
}DT;

为什么还有一个标记?这就涉及到我们找矩形是个什么样的形式,我们遍历数组,当遇到一个零件,它第一次出现(没有被标记),那么,它就必是这个编号零件所在矩形的右上角的顶点,以它开始,就能得到一个大略的矩形长和宽。然后再进行上文讲的判断,再判断过程中我们对每一个遍历元素打上标记,这样如果这一堆零件符合堆放条件,那么这么做的结果就是将这一大块的零件都标记上了,也就是说,之后再遍历这个元素时可以跳过不看。

接着我们定义一个结构体的二维数组,用来存储数据。输入编号和初始化标记。

int shuru(int hang , int lie , DT sz[][lie])//行,列,以及结构体二维数组
{
	int max = 0;
	int i , j;
	for(i = 0;i < hang;i ++)
	{
		for(j = 0;j < lie;j ++)
		{
			scanf("%1d" , &sz[i][j].num);
			max = max < sz[i][j].num ? sz[i][j].num : max;
			sz[i][j].bj = MBJ;//枚举常量
		}
	}
	return max;
}

我们可以注意到这里面为什么有个max?这边就可以开始解释如果矩形外出现了相同零件的问题。

对于每种零件,我们已经知道了它们必须呆在一块地方。所以,当一种零件符合了条件,它必然能在一次判断矩形的操作中被全部标记,然后就不会再出现。所以,当有其他同样的零件出现再两个地方时,这个编号的零件必然会被计数两次。

所以,我们可以用定义一个一维数组,用零件编号作为下标来对零件进行计数,如果同一个零件计数大于1时,则代表这种零件存在在不止一个位置,即不符合条件,直接返回否输出“NO”就行了。毕竟是以编号作为下标,所以这边必须统计出编号最大值,方便确定这个一维数组大小。(有点哈希表的味道)

同时,这边还使用了枚举常量

enum
{
	BCZ = 0,//不存在,表示这个编号零件不存在 
	MBJ = 1,//没标记,表示这个零件没被标记 
	BJL = 2,//标记了,表示这个零件被标记了 
};

这边来介绍找矩形的函数

int shu_jx(int hang , int lie , DT sz[][lie] , int hash[])
{
	int i , j;
	for(i = 0;i < hang;i ++)//行 
	{
		for(j = 0;j < lie;j ++)//列 
		{
			if(sz[i][j].bj == MBJ)//如果这个位置的零件没被标记 
			{
				if(hash[sz[i][j].num] == 1)//如果这个零件已经被计数了一次 
				{
					return 0;//出现在了第二个地方 
				}
				if(!pd_qy_jx(hang , lie , sz , i , j))//如果这个零件所在的区域的矩形不和条件 
				{
					return 0;
				}
				hash[sz[i][j].num] ++;//对零件计数 
			}
		}
	}
	return 1;//如果一路走下来都符合条件,则返回真 
}

遍历过程判断两块地方。这个零件有没有已经被计数了;这个零件所待的区域是否符合条件。

接下来就是对一个零件所在的矩形进行判断

int pd_qy_jx(int hang , int lie , DT sz[][lie] , int dq_hang , int dq_lie)//总行数和列数,总的数据,当前这个零件所在的行和列 
{
	int jx_chang = 0 , jx_kuan = 0;//从这个元素开始的长和宽 
	qiu_chang_kuan(dq_hang , hang , dq_lie , lie , sz , &jx_chang , &jx_kuan);//计算长和宽 
	
	int i , j;
	for(i = dq_hang;i < jx_kuan + dq_hang;i ++)
	{
		for(j = dq_lie;j < jx_chang + dq_lie;j ++)//遍历这个区域中的数据 
		{
			sz[i][j].bj = BJL;//首先先打上标记 
			if(sz[i][j].num != sz[dq_hang][dq_lie].num)//再判断是不是和开始位置的元素一样 
			{
				return 0;//如果不一样,则代表这个矩形区域内存在不止一种零件,不和条件 
			}
		}
	}
	return 1; 
}

其中还存在对于矩形区域长和宽的计算

void qiu_chang_kuan(int dq_hang , int hang , int dq_lie , int lie , DT sz[][lie] , int * jl_c , int * jl_k)//长和宽的指针 
{
	int i;
	for(i = dq_lie;i < lie;i ++)//从当前的列出发 
	{
		if(sz[dq_hang][i].num != sz[dq_hang][dq_lie].num)//如果遇到了不相同元素,即代表长度统计在这边结束 
		{
			break;
		}
		++ *jl_c;//长记录 
	}
	for(i = dq_hang;i < hang;i ++)
	{
		if(sz[i][dq_lie].num != sz[dq_hang][dq_lie].num)
		{
			break;
		}
		++ *jl_k;//宽记录 
	}
}

由此,问题解决。

完整代码

#include <stdio.h>
typedef struct data
{
	int num;//编号 
	int bj;//标记 
}DT;
enum
{
	BCZ = 0,//不存在,表示这个编号零件不存在 
	MBJ = 1,//没标记,表示这个零件没被标记 
	BJL = 2,//标记了,表示这个零件被标记了 
};
int pd_fl(void);
int shuru(int , int lie , DT [][lie]);
void chushihua(int , int []);
int shu_jx(int , int lie , DT [][lie] , int []);
int pd_qy_jx(int , int lie , DT [][lie] , int , int);
void qiu_chang_kuan(int , int , int , int lie , DT [][lie] , int * , int *);
int main (void)
{
	int cishu;
	scanf("%d" , &cishu);
	while(cishu -- > 0)
	{
		printf("%s\n" , pd_fl() ? "YES" : "NO");
	}
	return 0;
}

void qiu_chang_kuan(int dq_hang , int hang , int dq_lie , int lie , DT sz[][lie] , int * jl_c , int * jl_k)//长和宽的指针 
{
	int i;
	for(i = dq_lie;i < lie;i ++)//从当前的列出发 
	{
		if(sz[dq_hang][i].num != sz[dq_hang][dq_lie].num)//如果遇到了不相同元素,即代表长度统计在这边结束 
		{
			break;
		}
		++ *jl_c;//长记录 
	}
	for(i = dq_hang;i < hang;i ++)
	{
		if(sz[i][dq_lie].num != sz[dq_hang][dq_lie].num)
		{
			break;
		}
		++ *jl_k;//宽记录 
	}
}

int pd_qy_jx(int hang , int lie , DT sz[][lie] , int dq_hang , int dq_lie)//总行数和列数,总的数据,当前这个零件所在的行和列 
{
	int jx_chang = 0 , jx_kuan = 0;//从这个元素开始的长和宽 
	qiu_chang_kuan(dq_hang , hang , dq_lie , lie , sz , &jx_chang , &jx_kuan);//计算长和宽 
	
	int i , j;
	for(i = dq_hang;i < jx_kuan + dq_hang;i ++)
	{
		for(j = dq_lie;j < jx_chang + dq_lie;j ++)//遍历这个区域中的数据 
		{
			sz[i][j].bj = BJL;//首先先打上标记 
			if(sz[i][j].num != sz[dq_hang][dq_lie].num)//再判断是不是和开始位置的元素一样 
			{
				return 0;//如果不一样,则代表这个矩形区域内存在不止一种零件,不和条件 
			}
		}
	}
	return 1; 
}

int shu_jx(int hang , int lie , DT sz[][lie] , int hash[])
{
	int i , j;
	for(i = 0;i < hang;i ++)//行 
	{
		for(j = 0;j < lie;j ++)//列 
		{
			if(sz[i][j].bj == MBJ)//如果这个位置的零件没被标记 
			{
				if(hash[sz[i][j].num] == 1)//如果这个零件已经被计数了一次 
				{
					return 0;//出现在了第二个地方 
				}
				if(!pd_qy_jx(hang , lie , sz , i , j))//如果这个零件所在的区域的矩形不和条件 
				{
					return 0;
				}
				hash[sz[i][j].num] ++;//对零件计数 
			}
		}
	}
	return 1;//如果一路走下来都符合条件,则返回真 
}

void chushihua(int gs , int sz[])
{
	while(gs -- > 0)
	{
		*sz ++ = BCZ;
	}
}

int shuru(int hang , int lie , DT sz[][lie])
{
	int max = 0;
	int i , j;
	for(i = 0;i < hang;i ++)
	{
		for(j = 0;j < lie;j ++)
		{
			scanf("%1d" , &sz[i][j].num);
			max = max < sz[i][j].num ? sz[i][j].num : max;
			sz[i][j].bj = MBJ;
		}
	}
	return max;
}

int pd_fl()
{
	int hang , lie;
	scanf("%d%d" , &hang , &lie);
	DT map[hang][lie];
	int max = shuru(hang , lie , map);
	int hash[max + 1];
	chushihua(max + 1 , hash);
	return shu_jx(hang , lie , map , hash);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值