zoukankan      html  css  js  c++  java
  • [2019多校联考(Round 6 T3)]脱单计划 (费用流)

    [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
    ",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
    ",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();
    }
    
  • 相关阅读:
    c++ primer 中讲的顶层const 和 底层 const 理解
    github 0 学习
    MySQL 0 学习
    c++11 move构造函数和move operator 函数 学习
    c++11 多线程 1
    c++ 多线程 0
    学习 emplace_back() 和 push_back 的区别 emplace_back效率高
    crontab执行脚本失败问题
    lucene 排序
    maven 内置变量
  • 原文地址:https://www.cnblogs.com/birchtree/p/11626636.html
Copyright © 2011-2022 走看看