Description
由于原题看了好多遍才真正理解题意,这里讲一个简化版本
给出平面上的一个圆和n个点
现在要再这n个点之间两两连边
每个点只能被连一次,只有圆内和圆外之间距离不超过d的点可以互相连边
且如果存在三个圆内的未被连边的点x,y,z,与圆外一点w的距离不超过d
现在若想连接w和y,并且x与z的连线与w,y的连线相交,那么这个连线是不合法的
求最大的连线数目和方案
n<=1e3
Solution
显然最大匹配
考虑怎么处理这个鬼畜的限制
发现执行匈牙利算法的时候我们每个点都会按顺序匹配
于是我们队每个圆外的点,将圆内的点关于它做一次极角排序,得到它的遍历顺序
然后直接做匈牙利就可以解决这个神奇的限制得到答案
注意方案必须要按顺序,所以每个点必须等它之前的匹配全部输出之后才能输出
并不知道atan2用来极角排序哪里错了
bool cmp(point a,point b)
{return atan2(a.y-rt.y,a.x-rt.x)<atan2(b.y-rt.y,b.x-rt.x);}
改成叉积就对了,求dalao解释
bool cmp(point a,point b) {return cro(a,b,rt)>0;}
UPD:错因很显然然而我现在才发现一定是我太弱了_ (:з」∠) _
这道题排极角序必须是严格按顺序,不能有一个起点
然而用atan2就默认第3象限最小,这样会有问题
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define sqr(x) (x)*(x)
using namespace std;
typedef double db;
const int N=1e3+5;
int n,r,d,tot,ans,b[N],c[N],g[N][N],to[N],link[N];
db eps=1e-5;
bool vis[N],bz[N];
struct point{int x,y,id;}a[N],p[N],rt;
int cro(point a,point b,point x) {return (a.x-x.x)*(b.y-x.y)-(a.y-x.y)*(b.x-x.x);}
db dist(point a,point b) {return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));}
bool cmp(point a,point b) {return cro(a,b,rt)>0;}
int find(int x) {
fo(i,1,g[x][0])
if (!vis[g[x][i]]) {
vis[g[x][i]]=1;
if (!to[g[x][i]]||find(to[g[x][i]])) {
to[g[x][i]]=x;
return 1;
}
}
return 0;
}
int main() {
freopen("B.in","r",stdin);
scanf("%d%d%d",&n,&r,&d);
fo(i,1,n) scanf("%d%d",&a[i].x,&a[i].y),a[i].id=i;
fo(i,1,n) {
if (dist(a[i],a[1])-r<eps) b[++b[0]]=i;
else c[++c[0]]=i;
}
fo(i,1,c[0]) {
tot=0;rt=a[c[i]];
fo(j,1,b[0])
if (dist(a[b[j]],a[c[i]])-d<eps)
p[++tot]=a[b[j]];
sort(p+1,p+tot+1,cmp);int x=a[c[i]].id;
fo(j,1,tot) g[x][++g[x][0]]=p[j].id;
}
fo(i,1,c[0]) {
memset(vis,0,sizeof(vis));
ans+=find(a[c[i]].id);
}
printf("%d\n",ans*2);
fo(i,1,b[0]) link[to[a[b[i]].id]]=a[b[i]].id;
fo(i,1,ans) {
bool pd=0;int now=0;
fo(j,1,c[0]) {
int x=a[c[j]].id;
fo(k,1,g[x][0])
if (!bz[g[x][k]]) {
if (g[x][k]==link[x]) pd=1;
now=g[x][k];break;
}
if (pd) {
bz[now]=1;
printf("%d %d\n",x,now);
break;
}
}
}
}