AC通道:http://poj.org/problem?id=3565
题目大意:
给出n个白点和n个黑点的坐标,要求用n条不相交的线段把它们连接起来,其中每条线段恰好连接一个白点和一个黑点,每个点恰好连接到一条线段。如图所示。
第一行为整数n(1≤n≤100)。
以下n行每行包含两个整数,及各白点坐标;
以下n行每行包含两个整数,及各黑点坐标;
坐标均为绝对值不超过10000的整数。
输入保证没有两个坐标相同的点,且任意三点不共线。
所有白点和黑点均按照输入顺序编号为1~n。
输出n行,其中第i行为第i个白点所连接的黑点编号。
5
-42 58
44 86
7 28
99 34
-13 -59
-47 -44
86 74
68 -75
-68 60
99 -60
4
2
1
5
3
本题可以使用最佳完美匹配解决,请读者自行上网搜索。
我在这里介绍一种分治的方法。
首先,找出所有的点中纵坐标最小的点(如果纵坐标相同,就取横坐标小的点)记作X。
那么,对于所有除了它之外的点I,向量 XI→ 的极角就在[0,π)的范围内。
将这些点按照极角大小排个序。
若x与第一个点的颜色不同,那么就把它们相连,递归处理剩下的点。
反之,就接着往下扫描,直到黑点个数与白点个数相同。因为这个区间的连线是不会干扰到区间外的连线的,所以把这个区间的点递归处理,并递归处理这个区间外的点。
注意,当待处理的点的个数为0时,应停止递归。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
struct Point{
int x,y,z,num;
double ppp;
Point(int a=0,int b=0,int c=0):x(a),y(b),z(c){}
};
bool cmp(Point a,Point b){
return (a.y<b.y||(a.y==b.y&&a.x<b.x));
}
bool cmp2(Point a,Point b){
return a.ppp<b.ppp;
}
Point s[1000];
int w[1000];
int ans[1000];
int n;
void solve(int r){
if(r==0)return;
Point tmp[1001];
for(int i=1;i<=r;i++)tmp[i]=s[w[i]];
sort(tmp+1,tmp+r+1,cmp);
for(int i=2;i<=r;i++)tmp[i].ppp=atan2((double)tmp[i].y-tmp[1].y,(double)tmp[i].x-tmp[1].x);
sort(tmp+2,tmp+r+1,cmp2);
if(tmp[2].z!=tmp[1].z){
if(tmp[1].z)ans[tmp[1].num]=tmp[2].num;
else ans[tmp[2].num]=tmp[1].num;
for(int j=3;j<=r;j++)w[j-2]=tmp[j].num;
solve(r-2);
return;
}
int q1=1,q2=0,i=2;
while(q1!=q2){
i++;
if(tmp[i].z!=tmp[1].z)q2++;
else q1++;
}
for(int j=2;j<=q1*2+1;j++)w[j-1]=tmp[j].num;
solve(q1*2);
w[1]=tmp[1].num;
for(int j=q1*2+2;j<=r;j++)w[j-q1*2]=tmp[j].num;
solve(r-q1*2);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){scanf("%d%d",&s[i].x,&s[i].y);s[i].z=true;s[i].num=i;}
for(int i=n+1;i<=n*2;i++){scanf("%d%d",&s[i].x,&s[i].y);s[i].z=false;s[i].num=i;}
for(int i=1;i<=2*n;i++)w[i]=i;
solve(2*n);
for(int i=1;i<=n;i++)printf("%d\n",ans[i]-n);
return 0;
}