这是一道费用流的好题。
首先题目给你的男士女士以及之间的关系可以看做一个二分图。考虑暴力连边(用([x,y])表示流量为(x),费用为(y)的边):
- 源点像每个男士小区连([k,0])((k)表示小区的人数)
- 每个男士小区向每个女士小区连([INF,dis])((INF)为无穷大,(dis)为两个小区之间的曼哈顿距离)
- 每个女士小区向汇点连([k,0])
然后时间复杂度是(O(nmf)),不能通过此题。
考虑暴力连边边的数量是(O(n^2))级别的,是否能减少连边的数量。
现在假设每个男士小区和女士小区的距离为(x1+y1-x2-y2)(也就是去掉了绝对值的曼哈顿距离,其中(x1,y1)分别表示男士小区的横、纵坐标,(x2,y2)分别表示女士小区的横、纵坐标),那么我们可以建一个辅助点(P),男士小区向(P)连费用为(x1+y1)的边,(P)向女士小区连费用为(-x1-y1)的边,由于费用会累加的缘故,我们发现这样连边的效果和暴力是相同的,而边的数量却是(O(n))级别的。
那么加上绝对值该怎么做呢?我们可以分类讨论(|x1-x2|+|y1-y2|)的四种情况,建四个辅助点每个都向类似上文连边,发现如果某种连边方式如果与(|x1-x2|+|y1-y2|)的值并不相等也只能小于它,而我们求的是最大费用流,所以这些“错误”的边一定会被“正确”的边取代,从而保证答案的正确。
注:由于最后要求最大费用流,所以实际连边的时候费用应全部取相反数。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<deque>
using namespace std;
typedef long long LL;
const int N=5000;
const LL INF=1LL<<60;
deque <int> q;
int n,Male[N],Female[N],xM[N],yM[N],xF[N],yF[N],kM[N],kF[N],S,T;
int tot,head[N*10],cnt=1,inq[N],vis[N],cur[N],P[10];
LL dis[N],Mincost;
struct Edge
{
int nxt,to;
LL c,w;
}g[N*10];
void add(int from,int to,LL w,LL c)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
g[cnt].w=w;
g[cnt].c=c;
head[from]=cnt;
}
void ADD(int from,int to,LL w,LL c)
{
add(from,to,w,c),add(to,from,0,-c);
}
void init()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d %d %d",&xM[i],&yM[i],&kM[i]),Male[i]=++tot;
for (int i=1;i<=n;i++)
scanf("%d %d %d",&xF[i],&yF[i],&kF[i]),Female[i]=++tot;
}
int Dis1(int i,int type)
{
return type==1?xM[i]+yM[i]:xF[i]+yF[i];
}
int Dis2(int i,int type)
{
return type==1?xM[i]-yM[i]:xF[i]-yF[i];
}
int Dis3(int i,int type)
{
return type==1?-xM[i]+yM[i]:-xF[i]+yF[i];
}
int Dis4(int i,int type)
{
return type==1?-xM[i]-yM[i]:-xF[i]-yF[i];
}
void clear()
{
memset(vis,0,sizeof(vis));
memcpy(cur,head,sizeof(cur));
for (int i=1;i<=tot;i++)
dis[i]=INF;
}
int SPFA()
{
clear(),q.push_back(S);
while(!q.empty())
{
int x=q.front();q.pop_front();
inq[x]=0;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(dis[v]<=dis[x]+g[i].c||g[i].w<=0)
continue;
dis[v]=dis[x]+g[i].c;
if(!inq[v])
{
inq[v]=1;
if(!q.empty()&&dis[q.front()]>=dis[v])
q.push_front(v);
else
q.push_back(v);
}
}
}
return dis[T]!=INF;
}
LL dfs(int x,LL Min)
{
if(x==T)
return Min;
LL flow=0;
vis[x]=1;
for (int &i=cur[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(dis[v]!=dis[x]+g[i].c||g[i].w<=0||vis[v])
continue;
LL tmp=dfs(v,min(Min-flow,g[i].w));
flow+=tmp,g[i].w-=tmp,g[i^1].w+=tmp,Mincost+=tmp*g[i].c;
if(flow==Min)
break;
}
return flow;
}
int Dinic()
{
int ans=0;
while(SPFA())
ans+=dfs(S,INF);
return ans;
}
void work()
{
T=++tot;
for (int i=1;i<=4;i++)
P[i]=++tot;
for (int i=1;i<=n;i++)
{
ADD(S,Male[i],kM[i],0),ADD(Female[i],T,kF[i],0);
ADD(Male[i],P[1],INF,-Dis1(i,1)),ADD(P[1],Female[i],INF,-Dis4(i,0));
ADD(Male[i],P[2],INF,-Dis2(i,1)),ADD(P[2],Female[i],INF,-Dis3(i,0));
ADD(Male[i],P[3],INF,-Dis3(i,1)),ADD(P[3],Female[i],INF,-Dis2(i,0));
ADD(Male[i],P[4],INF,-Dis4(i,1)),ADD(P[4],Female[i],INF,-Dis1(i,0));
}
Dinic();
printf("%lld
",-Mincost);
}
int main()
{
init();
work();
return 0;
}