洛谷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;
}
上一篇:C++的数据类型操作 - map


下一篇:自学java的Second Day