【AtCoder Grand Contest 034 D】Manhattan Max Matching

费用流。

题目大意:

在二维平面上有 $n$ 组红球和 $n$ 组蓝球,每组球有若干个。保证两种颜色的球的总数相等。

现在要求红球和蓝球配对,一个配对的贡献为两球的曼哈顿距离。

求最大的贡献。

题解:

考虑两个点 $(x_1,y_1),(x_2,y_2)$,它们的曼哈顿距离为 $|x_1-x_2|+|y_1-y_2|$。把绝对值拆开,则有四种情况:

可以发现,对于一个点来说,它的两个坐标的贡献的正负性只有 $4$ 种情况,并且可以唯一确定和它匹配的点的坐标的正负性。

对于原题,我们如果直接连边跑二分图匹配,则边数是 $n^2$ 级别的,不能接受。

我们考虑对红球和蓝球分别建四个点,表示它们的横、纵坐标的正负情况,然后所有点值向四种对应情况连边。

这样涵盖了绝对值拆开后的四种情况。由于绝对值是取四种情况中最大的一种,而跑最大费用最大流得到的答案,取的是最大答案,所以取到的贡献是绝对值。

这样边数是 $O(n)$ 级别的。

Code:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef long long LL;
const int N=6e3,inf=1e9;
struct edge{
    int to,nxt,cap,cost;
}e[N*10];
int n,cnt=1,head[N],T,iter[N];
LL dist[N],ans;
bool vis[N];
queue<int>q;
inline void addedge(int u,int v,int cost,int cap){
    e[++cnt]=(edge){v,head[u],cap,cost},head[u]=cnt;
    e[++cnt]=(edge){u,head[v],0,-cost},head[v]=cnt;
}
int dfs(int u,int f){
    if(u==T||!f)return f;
    vis[u]=1;
    int ret=0;
    for(int&i=iter[u];i;i=e[i].nxt)
    if(e[i].cap&&!vis[e[i].to]&&dist[e[i].to]==dist[u]+e[i].cost){
        int d=dfs(e[i].to,min(f,e[i].cap));
        e[i].cap-=d,e[i^1].cap+=d,f-=d,ret+=d;
        if(!f)break;
    }
    vis[u]=0;
    return ret;
}
bool MCMF(){
    for(int i=1;i<=T;++i)dist[i]=-1e18;
    for(q.push(0);!q.empty();){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt)
        if(e[i].cap&&dist[e[i].to]<dist[u]+e[i].cost){
            dist[e[i].to]=dist[u]+e[i].cost;
            if(!vis[e[i].to])
            vis[e[i].to]=1,q.push(e[i].to);
        }
    }
    if(dist[T]<-1e17)return 0;
    memcpy(iter,head,sizeof(int)*(T+2));
    while(int f=dfs(0,inf))ans+=dist[T]*f;
    return 1;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    T=2*n+9;
    for(int i=1;i<=n;++i){
        int x,y,c;
        cin>>x>>y>>c;
        addedge(0,i,0,c);
        addedge(i,2*n+1,x+y,c);
        addedge(i,2*n+2,x-y,c);
        addedge(i,2*n+3,y-x,c);
        addedge(i,2*n+4,-x-y,c);
    }
    for(int i=n+1;i<=2*n;++i){
        int x,y,c;
        cin>>x>>y>>c;
        addedge(i,T,0,c);
        addedge(2*n+5,i,x+y,c);
        addedge(2*n+6,i,x-y,c);
        addedge(2*n+7,i,y-x,c);
        addedge(2*n+8,i,-x-y,c);
    }
    addedge(2*n+1,2*n+8,0,inf);
    addedge(2*n+2,2*n+7,0,inf);
    addedge(2*n+3,2*n+6,0,inf);
    addedge(2*n+4,2*n+5,0,inf);
    while(MCMF());
    cout<<ans<<'\n';
    return 0;
}