【题目链接】
【题目考点】
1. 图论:拓扑排序
【解题思路】
先理解题意:
如图,每张幻灯片是一个矩形,在该矩形范围内有一个位置写了这张幻灯片的编号。但实际情况是幻灯片是透明的而且可能是重叠的,编号的颜色都是相同的,一眼看上去看不出一个编号是写在哪张幻灯片上的。现在按照字母编号给出每张幻灯片矩形的位置、同时给出每个数字编号的位置,写出字母编号和数字编号的对应关系。
即给出A幻灯片上的编号是什么,B幻灯片上的编号是什么……
如果存在多种字母和编号的对应关系,或不存在可行的对应关系,都要输出None。
可以将幻灯片和数字编号都想像成顶点,如果编号X在幻灯片Y的范围内,那么顶点Y到顶点X有一条有向边。上图转为的拓扑图如图。
而后可以进行类似拓扑排序的操作:
先将所有入度为1的顶点入队。
每次循环出队一个顶点X,如果到达顶点X的边只有一条,是从顶点Y出发到顶点X的边,那么顶点X的入度为1,这意味着顶点X代表的编号只存在于顶点Y代表的幻灯片的范围内,因此顶点X代表的编号一定为顶点Y代表的幻灯片的编号。
而后同时把顶点X、顶点Y删掉,自然也把从Y出发的边都删掉,再看哪些顶点入度为1,将入度为1的顶点入队。
重复上述操作。
如果最后删掉的表示编号顶点的数量等于n,说明每张幻灯片都找到了与其对应的编号。
如果最后删掉的表示编号顶点的数量小于n,说明最后有一些编号不能确定到底算是哪个幻灯片的编号,编号和幻灯片有多种对应关系,或没有对应关系,应该输出None。
【题解代码】
解法1:拓扑排序
#include<bits/stdc++.h>
using namespace std;
#define N 30
struct Rect
{
int xmin, xmax, ymin, ymax;
bool isIn(int x, int y)
{
return x >= xmin && x <= xmax && y >= ymin && y <= ymax;
}
} slide[N];//slide[i]:字母编号为i的幻灯片
bool edge[N][N];//edge[i][j]:字母编号为i的幻灯片是否压着数字j
int n, deg[N], rec[N];//rec[i]:字母编号为i的幻灯片对应的数字
int find(int u)//对于入度只有1的数字顶点u,寻找与其对应的唯一字母顶点
{
for(int k = 0; k < n; ++k)
if(edge[k][u])//从字母k到数字u有边
return k;
}
bool topoSort()//返回是否成功完成类拓扑排序(删除数字结点数量是否为n)
{
int num = 0;//成功删除的数字结点数量
queue<int> que;
for(int i = 1; i <= n; ++i)//开始进行类拓扑排序,删除入度为1的数字顶点
if(deg[i] == 1)
que.push(i);
while(que.empty() == false)
{
int u = que.front(), k = find(u);//字母k指向数字u
que.pop();
rec[k] = u;//字母k与数字u对应
num++;
for(int i = 1; i <= n; ++i) if(edge[k][i])//删除从字母k到数字i的边
{
edge[k][i] = false;
if(--deg[i] == 1)//如果数字i入度为1,则入队
que.push(i);
}
}
return num == n;
}
int main()
{
int x, y;
cin >> n;
for(int i = 0; i < n; ++i)
cin >> slide[i].xmin >> slide[i].xmax >> slide[i].ymin >> slide[i].ymax;
for(int i = 1; i <= n; ++i)
{
cin >> x >> y;
for(int j = 0; j < n; ++j) if(slide[j].isIn(x, y))//如果x,y在幻灯片字母编号j的范围内
{
edge[j][i] = true;//幻灯片j压着数字i
deg[i]++;//数字编号i的入度增加
}
}
bool res = topoSort();
if(res)//配对成功
{
for(int i = 0; i < n; ++i)
cout << char('A'+i) << ' ' << rec[i] << endl;
}
else
cout << "None";
return 0;
}