资源限制
时间限制: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”。
图示:
1 | 1 | 1 | 1 | 1 | 3 |
1 | 1 | 1 | 1 | 1 | 3 |
1 | 1 | 1 | 1 | 1 | 3 |
1 | 1 | 1 | 1 | 1 | 3 |
1 | 1 | 1 | 1 | 1 | 3 |
0 | 0 | 2 | 2 | 2 | 3 |
0 | 0 | 2 | 2 | 2 | 3 |
0 | 0 | 2 | 2 | 2 | 3 |
如果我们先从第一行第一列开始,从当前行和当前列分别计数与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);
}