题意
一些透明的纸杂乱地摞在一起,从上往下依次用大写英文字母编号。每一张纸上都写着一个数字。现在给出纸的边框坐标和数字的坐标,试确定每张纸对应的数字是几。以成对形式输出确定对应的纸编号和数字。如果没有确定的对应,则输出none。
思路
一开始以为是一个简单的二分图模板,然而写出来样例都过不了
这里的“对应”并不是简单的匹配,而是一种确定关系。题意是说确定了某个数字一定在某一张纸上。然后就开始各种打补丁,然而还是没有AC。
看了网上的题解才知道,这种唯一的对应关系称为二分图匹配中的必须边。具体算法过程如下:
首先,根据题意,必定存在完美匹配。所以,先用二分图算法求出一个完美匹配。(这个结果令存一个数组)
然后对于这个完美匹配中的每一条边,在图中删除这条边,然后再进行二分图匹配。如果仍是完美匹配,说明这条边不是必须边,这两个点的关系也不是唯一的对应关系。如果不是完美匹配了,说明这条边是必须边。
题目链接
http://poj.org/problem?id=1486
AC代码
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 300;
int n; //纸的数量
int A[maxn][4]; //纸边缘坐标
int B[maxn][2]; //数字坐标
bool G[maxn][maxn]; //纸和数字抽象成点建图
int res[maxn]; //一开始求出的完美匹配
int match[maxn];
bool usd[maxn];
//二分图匹配模板
bool dfs(int v)
{
usd[v] = true;
for(int i= n; i< 2*n; i++)
{
if(!G[v][i]) continue;
int u = i, w = match[u];
if(w < 0 || !usd[w] && dfs(w))
{
match[v] = u;
match[u] = v;
return true;
}
}
return false;
}
int max_match()
{
int res = 0;
memset(match, -1, sizeof match);
for(int i= 0; i< n; i++)
if(match[i] < 0)
{
memset(usd, 0, sizeof usd);
if(dfs(i)) res ++;
}
return res;
}
//二分图匹配模板
int main()
{
for(int k= 1; ; k++)
{
scanf("%d", &n);
if(n == 0) break;
memset(G, false, sizeof G);
for(int i= 0; i< n; i++)
for(int j= 0; j< 4; j++)
cin >> A[i][j];
for(int i= 0; i< n; i++)
cin >> B[i][0] >> B[i][1];
for(int i= 0; i< n; i++)
{
for(int j= 0; j< n; j++)
{
if(A[i][0] < B[j][0] && A[i][1] > B[j][0]
&& A[i][2] < B[j][1] && A[i][3] > B[j][1])
{
G[i][n+j] = G[n+j][i] = true;
}
}
}
max_match();
//另存完美匹配结果
for(int i= 0; i< n; i++)
res[i] = match[i];
int cnt = 0;//已找出的必须边数
printf("Heap %d\n", k);
for(int i= 0; i< n; i++)
{
G[i][res[i]] = G[res[i]][i] = false;//在图中删除边
if(n == max_match()) continue;//仍是完美匹配
else{ //不是完美匹配了
if(cnt ++ > 0) printf(" ");
printf("(%c,%d)", i+'A', res[i]-n+1);
}
G[i][res[i]] = G[res[i]][i] = true;//判定完后恢复边
}
if(cnt == 0) printf("none");
cout << endl << endl;
}
return 0;
}