CCF201403-2 窗口 (100分)

问题描述
  在某图形操作系统中,有 N 个窗口,每个窗口都是一个两边与坐标轴分别平行的矩形区域。窗口的边界上的点也属于该窗口。窗口之间有层次的区别,在多于一个窗口重叠的区域里,只会显示位于顶层的窗口里的内容。
  当你点击屏幕上一个点的时候,你就选择了处于被点击位置的最顶层窗口,并且这个窗口就会被移到所有窗口的最顶层,而剩余的窗口的层次顺序不变。如果你点击的位置不属于任何窗口,则系统会忽略你这次点击。
  现在我们希望你写一个程序模拟点击窗口的过程。
输入格式
输入的第一行有两个正整数,即 N 和 M。(1 ≤ N ≤ 10,1 ≤ M ≤ 10)
  接下来 N 行按照从最下层到最顶层的顺序给出 N 个窗口的位置。 每行包含四个非负整数 x1, y1, x2, y2,表示该窗口的一对顶点坐标分别为 (x1, y1) 和 (x2, y2)。保证 x1 < x2, y1 < y2。
  接下来 M 行每行包含两个非负整数 x, y,表示一次鼠标点击的坐标。
  题目中涉及到的所有点和矩形的顶点的 x, y 坐标分别不超过 2559 和  1439。
问题分析:这个问题可以用链式线性表来实现。
输出格式:输出包括 M 行,每一行表示一次鼠标点击的结果。如果该次鼠标点击选择了一个窗口,则输出这个窗口的编号(窗口按照输入中的顺序从 1 编号到 N);如果没有,则输出"IGNORED"(不含双引号)。
样例输入
3 4
0 0 4 4
1 1 5 5
2 2 6 6
1 1
0 0
4 4
0 5
样例输出
2
1
1
IGNORED
样例说明
  第一次点击的位置同时属于第 1 和第 2 个窗口,但是由于第 2 个窗口在上面,它被选择并且被置于顶层。
  第二次点击的位置只属于第 1 个窗口,因此该次点击选择了此窗口并将其置于顶层。现在的三个窗口的层次关系与初始状态恰好相反了。
  第三次点击的位置同时属于三个窗口的范围,但是由于现在第 1 个窗口处于顶层,它被选择。
  最后点击的 (0, 5) 不属于任何窗口。

问题分析

类比

想想当我们使用鼠标点击时的情景,可以把一个个窗口看做一叠纸叠合(不一定是整齐叠 放)而成,这叠纸放在桌面上,把鼠标看成一根针,现在拿针去刺那叠纸,若是针直接扎到桌面上,说明“点击无效”;若扎到了纸,说明“点击有效”,则将第一张与针尖接触的纸抽出来,放在最顶部。联系数据结构,可将每张纸抽象成一个节点,使用单向链表存储

数据结构

逻辑结构:链式结构

物理结构:单向带头结点链表

节点设计:两组坐标,一个编号

算法

对于每一组鼠标点击坐标,若从顶窗口开始遍历窗口链表,若找到符合条件的窗口,立马返回该窗口编号;若无符合条件窗口,返回-1。返回值是为了便于判断输出时的值

代码实现

该题是为了练习数据结构中的单向带头结点链表,所以会一步步构建链表及其基本操作,按顺序实现如下函数(以下也是编程时步步为营的思想,反复迭代,最终解决问题):

  1. 初始化带头结点的空链表
  2. 添加指定个数的链表(使用头插法,为了适应题目:最后输入为顶窗口)
  3. 遍历链表(仅仅是为了测试链表是否创建成功)
  4. 模拟鼠标点击事件(核心功能)
  5. 主函数中的输出函数(比较简单)

C/C++

#include<iostream>
using namespace std;
#include<stdlib.h>

//窗口节点:包含一组对角坐标,窗口编号 
typedef struct LNode
{
	int x1, y1;
	
	int x2, y2;
	
	int id;
	
	struct LNode *next;
		
}LNode, *LinkList;

//初始化空链表 
void InitList(LinkList &L)
{
	L = (LinkList)malloc(sizeof(LNode));
	
	L->next = NULL;
	
}

//创建指定个数的链表 
//使用头插法建立链表,因为依题意可知最后输入的窗口作为顶层窗口 
//而鼠标点击也是从最顶开始考虑的 ,若是使用尾插法则遍历有些困难 
void CreateList(LinkList &L, int n)
{
	LNode *p;
	
	for(int i = 0; i < n; i++)
	{
		p = (LNode*)malloc(sizeof(LNode));
		
		cin >> p->x1 >> p->y1;
		
		cin >> p->x2 >> p->y2;
		
		p->id = i + 1;
		
		//插入头结点后第一个位置 
		p->next = L->next;
		
		L->next = p;	
	}
}

//遍历链表:在此仅仅是为了测试链表是否建立成功 
void TraverseList(LinkList L)
{
	if(L->next == NULL)
	{
		cout<<"空表!"<<endl;
		
		return;
		
	}
	
	LNode *pcur;
	
	pcur = L->next;
	
	while(pcur)
	{
		cout<<" id: "<<pcur->id<< " x1: "<<pcur->x1<<" x2: "<<pcur->y1
		
			<<" x2: "<<pcur->x2<<"  y2: "<<pcur->y2<<endl; 
			
		pcur = pcur->next;
	}
}

//模拟鼠标点击事件 
//从顶窗口开始遍历,若找到符合条件的窗口,立马返回该窗口编号;若无符合条件窗口,返回-1
//返回值是为了便于判断输出时的值 
int SimuClick(LinkList &L, int x, int y)
{
	LNode *pcur, *ppre;
	
	ppre = L;
	
	pcur = ppre->next;
	
	//从顶开始遍历窗口链表,这也是为什么使用头插法建立链表的原因 
	while(pcur)
	{
		//是否在当前窗口内(等于也包括在内) 
		//若该点击在当前窗口内,先将其移到头结点后第一位 
		if(x >= pcur->x1 && x <= pcur->x2 && y <= pcur->y2 && y >= pcur->y1)
		{	
			
			ppre->next = pcur->next;//防止断链 
			
			pcur->next = L->next;//移接到头结点后 
			
			L->next = pcur;
			
			return pcur->id;//直接返回编号 
			
		}
		
		else//不在当前窗口内,继续查找 
		{
			ppre = pcur;
			
			pcur = pcur->next;
			
		}
	}
	
	return -1;//若是执行到此步,说明没有找到一个窗口包含此次点击,返回一个标志 
}

int main()
{
	LinkList L;
	
	int n, m, x, y;
	
	InitList(L);	
	
	cin >> n >> m;
	
	CreateList(L , n);
//		TraverseList(L);

	int state[m+1];//为了记录输出时的内容 
	
	//输入一次点击就计算一次 
	for(int i = 0; i < m; i++)
	{
		cin >> 	x >> y;
		
		state[i] = SimuClick(L, x, y);
	}

	
	//输出结果 
	for(int i = 0; i < m; i++)
	{
		if(state[i] == -1)
		{
			cout<<"IGNORED"<<endl;//英文单词千万别打错,最好直接复制 
		}
		else
		{
			cout<<state[i]<<endl;
		}
	}
	return 0;
} 

体会

  • 读题后,立马跟着用例跑一遍,确认理解无误,若是比较难理解,可以先使用类比方法帮助理解
  • 抽象出对象,找出关系,再选择数据结构,再调整算法,代码实现是需清醒,考虑边界问题
  • 做题时需注意每个细节,如输出的单词不可拼写错误
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值