码迷,mamicode.com
首页 > 其他好文 > 详细

[2019多校联考(Round 6 T3)]脱单计划 (费用流)

时间:2019-10-06 11:05:46      阅读:59      评论:0      收藏:0      [点我收藏+]

标签:ret   cti   平面   algo   return   表示   network   ace   using   

[2019多校联考(Round 6 T3)]脱单计划 (费用流)

题面

你是一家相亲机构的策划总监,在一次相亲活动中,有 n 个小区的若干男士和 n个小区的若干女士报名了这次活动,你需要将这些参与者两两匹配(只能男生和 女生相匹配),每个小区都提供了自己的地址,用二维平面上的坐标(x,y)来表示,若 A 男所在小区的地址为(x1,y1),B 女所在小区的地址为(x2,y2),由“距离产生美”可得,A 男不 B 女匹配的亲密值为他们的曼哈顿距离|x1-x2|+|y1-y2|,现在要求你确定一种匹配方案使得总亲密值最大(每位男士只能匹配一位女士,每位女士也只能匹配一位男士)

分析

此题和[AGC 034D]Manhattan Max Matching几乎一模一样

小区之间两两连容量无穷,费用为两点间曼哈顿距离的边,原点到男士所在小区连容量为该小区男士数量,费用为0的边。女士所在小区到汇点同理。这样显然是会超时的。

考虑简化的情况,如果费用为\(x_1-x_2+y_1-y_2\),那么可以建一个辅助点u,\((x_1,y_1)\)男士对应的点向u连费用为\(x_1+y_1\)的边,u向女士\((x_2,y_2)\)连费用为\(-x_2-y_2\)的边。跑费用流的时候费用叠加,就得到了\(x_1-x_2+y_1-y_2\)。这样连边的边数是\(O(n)\)

有绝对值符号怎么办。把绝对值按符号拆成4种情况。\(|x_1-x_2|+|y_1-y_2|=max(x_1-x_2+y_1-y_2,x_2-x_1+y_1-y_2,x_1-x_2+y_2-y_1,x_2-x_1+y_2-y_1)\)

建4个辅助点对应4种情况,每个点都像上面那样连边。

由于是最大费用,跑出来的是4种情况最大值,恰好就是曼哈顿距离取了绝对值符号后的结果。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
#include<cstdlib> 
#include<queue>
#define INF 0x3f3f3f3f3f3f3f3f 
#define maxn 10000
#define maxm 3000000 
using namespace std;
typedef long long ll;
int n,m;
inline void qread(int& x){
    x=0;
    int sign=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') sign=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
    x=x*sign;
}
inline void qread(ll& x){
    x=0;
    int sign=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') sign=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
    x=x*sign;
}

struct node{
    ll x;
    ll y;
    int c;
}a[maxn+5],b[maxn+5];
    
inline ll get_dist(node p,node q){
    return abs(p.x-q.x)+abs(p.y-q.y);
}





namespace network_flow{
    struct edge{
        int from;
        int to;
        int next;
        ll flow;
        ll cost;
    }E[maxm+5];
    int head[maxn+5]; 
    int sz=1;
    void add_edge(int u,int v,ll w,ll c){
#ifdef DEBUG
//      printf("%d->%d vol=%lld cost=%lld\n",u,v,w,c);  
#endif
        c=-c;
        sz++;
        E[sz].from=u;
        E[sz].to=v;
        E[sz].flow=w;
        E[sz].cost=c;
        E[sz].next=head[u];
        head[u]=sz;
        sz++;
        E[sz].from=v;
        E[sz].to=u;
        E[sz].flow=0;
        E[sz].cost=-c;
        E[sz].next=head[v];
        head[v]=sz;
    }
    
    bool inq[maxn+5];
    int pre[maxn+5]; 
    ll minf[maxn+5];
    ll dist[maxn+5]; 
    bool spfa(int s,int t){
        for(int i=s;i<=t;i++){
            inq[i]=0;
            pre[i]=0;
            dist[i]=INF;
            minf[i]=INF;
        } 
        queue<int>q;
        q.push(s);
        inq[s]=1;
        dist[s]=0;
        while(!q.empty()){
            int x=q.front();
            q.pop();
            inq[x]=0;
            for(int i=head[x];i;i=E[i].next){
                int y=E[i].to;
                if(E[i].flow&&dist[y]>dist[x]+E[i].cost){
                    dist[y]=dist[x]+E[i].cost;
                    pre[y]=i;
                    minf[y]=min(minf[x],E[i].flow);
                    if(!inq[y]){
                        q.push(y);
                        inq[y]=1;
                    }
                }
            }
        }
        return dist[t]!=INF;
    }
    void update(int s,int t){
        int x=t;
        while(x!=s){
            int i=pre[x];
            E[i].flow-=minf[t];
            E[i^1].flow+=minf[t];
            x=E[i].from;
        }
    }
    ll mcmf(int s,int t){
        
        ll flow=0,cost=0;
        while(spfa(s,t)){
            flow+=minf[t];
            cost+=minf[t]*dist[t];
            update(s,t);
        } 
        return -cost;
    }
    void solve(){
        int s=0,t=n*2+5;
        int p1=n*2+1,p2=n*2+2,p3=n*2+3,p4=n*2+4;
        for(int i=1;i<=n;i++){
            add_edge(s,i,a[i].c,0);
        }
        for(int i=1;i<=n;i++){
            add_edge(i+n,t,b[i].c,0);
        }
        //绝对值分符号拆成4个,用4个辅助点连边
        //因为是最大费用,4种情况取最大值就是绝对值
        //这样就把边数从O(n^2)变成O(n) 
        for(int i=1;i<=n;i++){
            //x1-x2+y1-y2
            add_edge(i,p1,INF,a[i].x+a[i].y); 
            add_edge(p1,i+n,INF,-b[i].x-b[i].y); 
            //x1-x2+y2-y1 
            add_edge(i,p2,INF,a[i].x-a[i].y); 
            add_edge(p2,i+n,INF,-b[i].x+b[i].y); 
            //x2-x1+y1-y2
            add_edge(i,p3,INF,-a[i].x+a[i].y); 
            add_edge(p3,i+n,INF,b[i].x-b[i].y); 
            //x2-x1+y2-y1
            add_edge(i,p4,INF,-a[i].x-a[i].y); 
            add_edge(p4,i+n,INF,b[i].x+b[i].y);  
        } 
        printf("%lld\n",mcmf(s,t)); 
    }
}



int main(){
//  freopen("1.in","r",stdin);
    qread(n);
    for(int i=1;i<=n;i++){
        qread(a[i].x);
        qread(a[i].y);
        qread(a[i].c);
    } 
    for(int i=1;i<=n;i++){
        qread(b[i].x);
        qread(b[i].y);
        qread(b[i].c);
    } 
    network_flow::solve();
}

[2019多校联考(Round 6 T3)]脱单计划 (费用流)

标签:ret   cti   平面   algo   return   表示   network   ace   using   

原文地址:https://www.cnblogs.com/birchtree/p/11626636.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!