【题目描述】
李教授将于今天下午作一次非常重要的演讲。不幸的是他不是一个非常爱整洁的人,他把自己演讲要用的幻灯片随便堆在了一起。因此,演讲之前他不得不去整理这些幻灯片。作为一个讲求效率的学者,他希望尽可能简单地完成它。教授这次演讲一共要用n张幻灯片(n<=26),这n张幻灯片按照演讲要使用的顺序已经用数字1~n编了号。因为幻灯片是透明的,所以我们不能一下子看清每一个数字所对应的幻灯片。
现在我们用大写字母A,B,C……再次把幻灯片依次编号。你的任务是编写一个程序,把幻灯片的数字编号和字母编号对应起来,显然这种对应应该是唯一的;若出现多种对应的情况或是某些数字编号和字母编号对应不起来,我们称对应是无法实现的。(p422)
【输入】
第一行只有一个整数n,表示有n张幻灯片,接下来的n行每行包括4个整数xmin,xmax,ymin,ymax(整数之间用空格分开)为幻灯片的坐标,这n张幻灯片按其在文件中出现的顺序从前到后依次编号为A,B,C……,再接下来的n行依次为n个数字编号的坐标x,y,显然幻灯片之外是不会有数字的。
【输出】
若是对应可以实现,输出文件应该包括n行,每一行为一个字母和一个数字,中间以一个空格隔开,并且每行以字母的升序排列,注意输出的字母要大写并且定格;反之,若是对应无法实现,在文件的第一行顶格输出None即可。首行末无多余的空格。
【输入样例】
4
6 22 10 20
4 18 6
16
8 20 2 18
10 24 4 8
9 15
19 17
11 7
21 11
【输出样例】
A 4
B 1
C 2
D 3
这道题是一个类似拓扑排序问题。在拓扑排序中,我们始终选择入度为0的点,而在这道题中,我们需要选择度最小的点v,如果degree(v) = 1, 那么就可能存在唯一解。 如果degree(v) = 0, 那么一定不存在可行解,如果degree(v) > 1,那么可以证明一定存在多种可行解。
本人写的代码并不按照题目的要求输出,而是输出所有的可行解。我使用dfs找出所有的可行解。记得在dfs后退时要恢复删除的边和匹配情况。
#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
using namespace std;
#define PPTNUM 26
int match[PPTNUM];
int pptsize[PPTNUM][4];
bool map[PPTNUM][PPTNUM];
int n;
// 寻找还没有匹配上数字的、且度数最小的ppt
int minDegreePPT()
{
int point = -1;
int minDegree = PPTNUM + 1;
for(int i = 0; i < n; i++)
{
if(match[i] == -1)
{
int degree = 0;
for(int j = 0; j < n; j++)
{
if(map[i][j])
degree++;
}
if(minDegree > degree)
{
minDegree = degree;
point = i;
}
}
}
return point;
}
void run(int step, int &ans)
{
if(step > n)
{
ans++;
for(int i = 0; i < n; i++)
{
printf("%c %d\n", i + 'A', match[i]+1);
}
printf("\n");
return;
}
int point = minDegreePPT();
vector<int> matchPoint;
for(int i = 0; i < n; i++)
{
if(map[point][i])
{
map[point][i] = false; // 删除与point相连的边
matchPoint.push_back(i); // 记录被删除的边
}
}
if(matchPoint.size() == 0)
{
// 找不到匹配
} else{
for(int i = 0; i < matchPoint.size(); i++)
{
int selectNum = matchPoint.at(i);
match[point] = selectNum;
// 删除与selectNum相连的边
vector<int> selectNumEdge;
for(int i = 0; i < n; i++)
{
if(map[i][selectNum])
{
selectNumEdge.push_back(i);
map[i][selectNum] = false;
}
}
run(step+1, ans);
// 恢复与selectNum相连的边
for(int i = 0; i < selectNumEdge.size(); i++)
{
map[selectNumEdge.at(i)][selectNum] = true;
}
match[point] = -1;
}
}
// 恢复与point相连的边
for(int i = 0; i < matchPoint.size(); i++)
{
map[point][matchPoint.at(i)] = true;
}
}
int main()
{
freopen("data.txt", "r", stdin);
while(cin >> n)
{
memset(match, -1, sizeof(match));
memset(pptsize, 0, sizeof(pptsize));
memset(map,false, sizeof(map));
for(int i = 0; i < n; i++)
{
cin >> pptsize[i][0] >> pptsize[i][1] >> pptsize[i][2] >> pptsize[i][3];
}
int x, y;
for(int i = 0; i < n; i++)
{
cin >> x >> y;
for(int j = 0; j < n; j++)
{
if(pptsize[j][0] <= x && x <= pptsize[j][1] && pptsize[j][2] <= y && y <= pptsize[j][3])
map[j][i] = true;
}
}
int result = 0;
run(1, result);
//cout << "result = " << result << endl;
if(result == 0 || result > 1)
{
cout << "None" << endl;
}
else {
/*
for(int i = 0; i < n; i++)
{
printf("%c %d\n", i + 'A', match[i]);
}
*/
printf("%d\n", result);
}
}
return 0;
}