题目抽象:
屏幕是一个N*M的矩阵,在屏幕上有一些“窗口”,每个窗口都是矩形的。窗口的边框用同一个大写字母来表示,不同的窗口的大写字母必定不同(窗口最多不超过26个)。如图1所示,大写字母A组成的边框表示屏幕上的一个窗口。
窗口可以互相重叠,那样的话,被遮挡的部分就看不到了,而被遮挡的那一部分边框也不会显示在屏幕上,如图2所示,窗口A和窗口B重叠,B在A上,A窗口的右下角被遮挡住了。
由于窗口的重叠,有些窗口的有些部分被其他窗口覆盖。但是,肯定就有一些窗口在最顶端,不被其他任何窗口覆盖,我们称这些窗口为“顶端窗口”。
输入数据要满足以下三点:
1.至少有一个“顶端窗口”
2.每个窗口的“宽”和“高”至少为3个字符
3.没有任何窗口的任何部分会超出屏幕
解题思路:
首先我们考虑到窗口是一个位置坐标的集合,然后这些坐标组成了一个长方形边框,那么这个窗口是一个顶端窗口。所以我们需要记录下该字母的所有坐标位置,并且得到坐标位置中横坐标的最小值x1,横坐标的最大值x2,纵坐标的最小值y1,纵坐标的最大值y2.所以要求这部分坐标集合满足(其中之一就行):
1.x=x1或x=x2,且y1<=y<=y2
2.y=y1或y=y2,且x1<=x<=x2
但是这部分边框还是存在一些漏洞:
其中的A被窗口B遮住,只看到一条线,但还是满足上述条件之一,所以要在原来的判断中增加关系式“x2-x1>=2,y2-y1>=2”,保证窗口的宽还有高都大于等于3.
另外还有一点需要注意的,就是出现这种情况:
虽然A的边框是完整的,但是窗口B在A上,所以A的内部是被窗口B覆盖的,因此窗口A并不是顶端窗口,所以要加判断式:
检验x1<x<x2且y1<y<y2中是否有大写字母。
总结一下:
对每个字母进行如下计算:
1.该字母出现的坐标位置全部保存在集合S中。
2.然后求S中横坐标最小值x1,横坐标最大值x2,纵坐标最小值y1,纵坐标最大值y2
3.判断是否有x2-x1>=2,y2-y1>=2(矩阵宽和高都要大于等于3)
4.判断是否满足下面两个条件之一的所有坐标(x,y)
x=x1或x=x2,且y1<=y<=y2
y=y1或y=y2,且x1<=x<=x2
5.判断满足下列条件的坐标(x,y)处都不是大写字母
x1<x<x2,y1<y<y2
6.如果上述判断正确,就输出该字母所在集合就是一个顶端窗口
#include <iostream>
#include <vector>
using namespace std;
const int big=10005;//定义无穷大
const int maxn=105;//屏幕最大边长
struct aaa
{
int x,y;
};//这里是定义了一种结构类型,方便储存坐标位置x,y
vector<aaa> c;//定义了结构类型为aaa的向量c,这里其实跟queue队列差不过情况
int n,m;//这里的n,m表示的是输入的行数还有列数
char a[maxn][maxn];//保存了屏幕中每个元素的坐标位置
void make()
{
char ch;//这里遍历的是字符A到Z
int i,j,minx,maxx,miny.maxy;//其中的minx,maxx,miny,maxy分别保存收集到的某一字母的整个窗口坐标的最小横坐标,最大横坐标,最小纵坐标,最大纵坐标
aaa node;//用来将坐标中的,x,y传入到向量之中
bool f;//成功标志f
for (ch='A';ch<='Z';ch++)//这里枚举了每一个大写字母
{
c.clear();//先将向量c中原来的数据全部清除掉,也就是每一次循环的时候就进行一次清除
for(i=1;i<=n;i++)
for(j=1;j<=m;j++) if(a[i][j]==ch)//这里的意思就是遍历了整个输入的n*m屏幕,找到里面所有满足值为ch的坐标
{
node.x=i;node.y=j;//将此时该字符的横纵坐标赋值给node
c.push_back(node);//将此时的node存入到向量c中
}
if(c.size()==0) continue;//说明此时的该字母可能完全被覆盖掉了或者根本就没有该字母的存在,跳出内部,继续循环
minx=big;miny=big;maxx=0;maxy=0;
for(i=0;i<c.size();i++)
{
if(c[i].x<minx) minx=c[i].x;//遍历得到x坐标最小值
if(c[i].y<miny) miny=c[i].y;//遍历得到y坐标最小值
if(c[i].x>maxx) maxx=c[i].x;//遍历得到x坐标最大值
if(c[i].y>maxy) maxy=c[i].y;//遍历得到y坐标最大值
}
if(maxx<=minx+1) continue;//就是说x坐标最大值与最小值之间不满足两者相减大于等于2的条件
if(maxy<=miny+1) continue;//就是说y坐标最大值与最小值之间不满足两者相减大于等于2的条件
if(c.size()!=(maxx-minx+maxy-miny)*2) continue;//说明此时存入向量的窗口不是完整窗口
f=true;//能进行到这一步说明该字符窗口肯定是一个完整的且大小符合的
for(i=minx+1;i<maxx;i++)//遍历符合题意的窗口内部是否有大写字母,如果没有就说明其是顶端窗口否则就是被覆盖的
for(j=miny+1;j<maxy;j++)
if(isupper(a[i][j]) f=false;//其中的isupper()函数是用来判断字符a[i][j]是否是大写字母,如果是,那么肯定不是顶端窗口了
if(f) cout<<ch;//如果最后f还是true的话,那么就输出这个字符
}
cout<<endl;
}
int main()
{
int i,j;
while (true)//多次循环利用该过程
{
cin>>n>>m;//分别输入n行数,m列数
if(n+m==0) break;//说明此时的行数与列数中肯定为0或者其中有一个为负数,不符合实际意义
for(i=1;i<=n;i++)
for(j=1;j<=m;j++) cin>>a[i][j];//通过两重循环输入了整个屏幕不同坐标的值
make();//就是这个有点意思的函数判别出了整个屏幕中的顶端窗口
}
return 0;
}