蚂蚁(Ants,NEERC 2008,LA4043)

AC通道:http://poj.org/problem?id=3565

蚂蚁

Description

题目大意:
给出n个白点和n个黑点的坐标,要求用n条不相交的线段把它们连接起来,其中每条线段恰好连接一个白点和一个黑点,每个点恰好连接到一条线段。如图所示。
蚂蚁(Ants,NEERC 2008,LA4043)_第1张图片

Input

第一行为整数n(1≤n≤100)。
以下n行每行包含两个整数,及各白点坐标;
以下n行每行包含两个整数,及各黑点坐标;
坐标均为绝对值不超过10000的整数。
输入保证没有两个坐标相同的点,且任意三点不共线。
所有白点和黑点均按照输入顺序编号为1~n。

Output

输出n行,其中第i行为第i个白点所连接的黑点编号。

Sample Input

5
-42 58
44 86
7 28
99 34
-13 -59
-47 -44
86 74
68 -75
-68 60
99 -60

Sample Output

4
2
1
5
3

Solution

本题可以使用最佳完美匹配解决,请读者自行上网搜索。
我在这里介绍一种分治的方法。

首先,找出所有的点中纵坐标最小的点(如果纵坐标相同,就取横坐标小的点)记作X。
那么,对于所有除了它之外的点I,向量 XI 的极角就在[0,π)的范围内。

将这些点按照极角大小排个序。
若x与第一个点的颜色不同,那么就把它们相连,递归处理剩下的点。
反之,就接着往下扫描,直到黑点个数与白点个数相同。因为这个区间的连线是不会干扰到区间外的连线的,所以把这个区间的点递归处理,并递归处理这个区间外的点。

注意,当待处理的点的个数为0时,应停止递归。

Code

#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;
}

你可能感兴趣的:(分治)