洛谷P1500
Description
给出 \(n\) 男 \(n\) 女。每个人都自己的坐标。
其中若干对男女之间有特定的缘分值。
一男一女能够匹配的条件是其直线距离不大于 \(k\) 且连线上不存在别的人。
求能匹配的最大缘分值。
Solution
本题采用 dinic 建负边求最大费用最大流。
思路很简单,就是二分图最大权匹配。对于每对男女,判断其能否配对,能则连费用为缘分值的边。然后源点连男,汇点连女即可。因为中国实行的是一夫一妻制,所以所有边的容量均为 \(1\)。
重点在怎样判断是否符合条件,这里考虑用斜率判断。
假设现在有三个点 \((x_1,y_1),(x_2,y_2),(x_3,y_3)\),分别记为点 \(a,b,c\)。我们钦定 \(x_1\le x_3\),现在要判断 \(b\) 是否在 \(ac\) 上。
显然,只需要判断 \(\large\frac{x_3-x_2}{y_3-y_2}\) 是否等于 \(\large\frac{x_2-x_1}{y_2-y_1}\) 即可,但是此时必须要满足 \(x_1 \le x_2\le x_3\) 的条件。对于不满足这个条件的点 \(b\),显然也不可能在 \(ac\) 上,特判一下即可。
对于字符串的处理,注意大小写不区分,存储推荐使用 map
。
具体实现见下方代码。
Code
省去了 dinic 的板子,只给出主函数建图以及判断部分的代码。
bool vis[maxn];
int love[maxn][maxn];//缘分值
int n,m,tot=1,k,res,s,t;
int head[maxn],cur[maxn],Dis[maxn];
struct edge{int fr,to,dis,cost,nxt;}e[maxn*1000];
map<int,string> val;//val[i] 编号为 i 的人的名字
map<string,int> pre;//pre[s] 名字为 s 的人的编号
map<string,pair<int,int> > pos;
//pos[s].first/second 名字为 s 的人的横/纵坐标
bool judge(int a,int b){
pair<int,int> A=pos[val[a]];
pair<int,int> B=pos[val[b]];
if(A.first>B.first)swap(a,b),swap(A,B);
for(int i=1;i<=n;i++){
if(i==a||i==b) continue;
if(pos[val[i]].first<A.first||pos[val[i]].first>B.first) continue;
if((double)(pos[val[i]].second-A.second)/(double)(pos[val[i]].first-A.first)
==(double)(B.second-pos[val[i]].second)/(double)(B.first-pos[val[i]].first))
return false;
}
if((A.first-B.first)*(A.first-B.first)+(A.second-B.second)*(A.second-B.second)>k*k) return false;
return true;
}
int main(){
k=read();n=read();s=n*2+1,t=s+1;
for(int i=1,x,y;i<=n;i++){
string name;cin>>x>>y>>name;
for(int j=0;j<name.length();j++)
if(name[j]>='a'&&name[j]<='z')
name[j]+='A'-'a';
pos[name]=make_pair(x,y);
val[i]=name;pre[name]=i;
}
for(int i=n+1,x,y;i<=n*2;i++){
string name;cin>>x>>y>>name;
for(int j=0;j<name.length();j++)
if(name[j]>='a'&&name[j]<='z')
name[j]+='A'-'a';
pos[name]=make_pair(x,y);
val[i]=name;pre[name]=i;
}
while(1){
string a,b;int v;
cin>>a;if(a=="End") break;cin>>b>>v;
for(int j=0;j<a.length();j++)if(a[j]>='a'&&a[j]<='z')a[j]+='A'-'a';
for(int j=0;j<b.length();j++)if(b[j]>='a'&&b[j]<='z')b[j]+='A'-'a';
love[pre[a]][pre[b]]=love[pre[b]][pre[a]]=v;
}
for(int i=1;i<=n;i++){
for(int j=n+1;j<=(n<<1);j++){
if(!judge(i,j)) continue;
if(!love[i][j]) love[i][j]=1;
add(i,j,1,-love[i][j]);
}
}
for(int i=1;i<=n;i++)
add(s,i,1,0),add(i+n,t,1,0);
dinic();printf("%d\n",-res);
return 0;
}