zoukankan      html  css  js  c++  java
  • [AGC034D]Manhattan Max Matching:费用流

    前置姿势

    (k)维空间内两点曼哈顿距离中绝对值的处理

    戳这里:[CF1093G]Multidimensional Queries

    多路增广的费用流

    据说这个东西叫做ZKW费用流?

    流程其实很简单,就是把EK中的单路回溯改成利用DFS多路增广,类似Dinic那样,可以看作是EK的一个优化。需要注意的是要标记从源点到当前点的路径,以免陷入零环无法自拔

    代码

    bool spfa(){
    	memset(dis,0x3f,sizeof dis);
    	rin(i,1,n)cur[i]=head[i];
    	while(!q.empty())q.pop();
    	dis[s]=0,book[s]=true;q.push(s);
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		trav(i,x){
    			int ver=e[i].to;
    			if(e[i].cap&&dis[ver]>dis[x]+e[i].cost){
    				dis[ver]=dis[x]+e[i].cost;
    				if(!book[ver]){
    					book[ver]=true;
    					q.push(ver);
    				}
    			}
    		}
    		book[x]=false;
    	}
    	return dis[t]<1e9;
    }
    
    int dfs(int x,int pref){
    	if(x==t||!pref)return pref;
    	int temp=0,flow=0;insta[x]=true;
    	for(int &i=cur[x];i;i=e[i].nxt){
    		int ver=e[i].to;if(insta[ver])continue;
    		if(dis[ver]==dis[x]+e[i].cost&&(temp=dfs(ver,std::min(pref,e[i].cap)))){
    			e[i].cap-=temp;
    			e[i^1].cap+=temp;
    			flow+=temp;
    			pref-=temp;
    			mincost+=temp*e[i].cost;
    			if(!pref)break;
    		}
    	}
    	insta[x]=false;
    	return flow;
    }
    
    void ek(){
    	while(spfa())maxflow+=dfs(s,1e9);
    }
    

    分析

    显然费用流,直接建图的话每一对红球和蓝球(的位置)都需要连边,一共要连(O(N^2))条边,再跑费用流的话显然时间爆炸。

    注意到把绝对值拆开后((x,y))系数只有(2^2)种可能性,并且两个球的距离是这四种情况中最大值,所以我们可以对四种情况分开处理,因为我们知道一对匹配的费用取的一定是最大值,即这两个球的距离。

    具体如何建图可以参考代码。

    代码

    #include <bits/stdc++.h>
    
    #define rin(i,a,b) for(int i=(a);i<=(b);++i)
    #define irin(i,a,b) for(int i=(a);i>=(b);--i)
    #define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
    #define Size(a) (int)a.size()
    #define pb push_back
    #define mkpr std::make_pair
    #define fi first
    #define se second
    #define lowbit(a) ((a)&(-(a)))
    typedef long long LL;
    
    using std::cerr;
    using std::endl;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    
    const int MAXN=1005;
    
    int n,s,t,ecnt=1,head[MAXN<<1];
    
    struct Edge{
    	int to,nxt,cap,cost;
    }e[MAXN*20];
    
    inline void add_edge(int bg,int ed,int ca,int co){
    	++ecnt;
    	e[ecnt].to=ed;
    	e[ecnt].nxt=head[bg];
    	e[ecnt].cap=ca;
    	e[ecnt].cost=co;
    	head[bg]=ecnt;
    }
    
    int maxflow,cur[MAXN<<1];
    LL mincost,dis[MAXN<<1];
    bool book[MAXN<<1],insta[MAXN<<1];
    std::queue<int> q;
    
    bool spfa(){
    	memset(dis,0x3f,sizeof dis);
    	rin(i,1,t)cur[i]=head[i];
    	while(!q.empty())q.pop();
    	dis[s]=0,book[s]=true;q.push(s);
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		trav(i,x){
    			int ver=e[i].to;
    			if(e[i].cap&&dis[ver]>dis[x]+e[i].cost){
    				dis[ver]=dis[x]+e[i].cost;
    				if(!book[ver]){
    					book[ver]=true;
    					q.push(ver);
    				}
    			}
    		}
    		book[x]=false;
    	}
    	return dis[t]<1e18;
    }
    
    int dfs(int x,int pref){
    	if(x==t||!pref)return pref;
    	int temp=0,flow=0;insta[x]=true;
    	for(int &i=cur[x];i;i=e[i].nxt){
    		int ver=e[i].to;if(insta[ver])continue;
    		if(dis[ver]==dis[x]+e[i].cost&&(temp=dfs(ver,std::min(pref,e[i].cap)))){
    			e[i].cap-=temp;
    			e[i^1].cap+=temp;
    			flow+=temp;
    			pref-=temp;
    			mincost+=1ll*temp*e[i].cost;
    			if(!pref)break;
    		}
    	}
    	insta[x]=false;
    	return flow;
    }
    
    void ek(){
    	while(spfa())maxflow+=dfs(s,1e9);
    }
    
    int main(){
    	n=read();
    	int x0=n*2+1,x1=x0+1,x2=x1+1,x3=x2+1;
    	s=x3+1,t=s+1;
    	rin(i,1,n){
    		int rx=read(),ry=read(),rc=read();
    		add_edge(s,i,rc,0);
    		add_edge(i,s,0,0);
    		add_edge(i,x0,1e9,rx+ry);
    		add_edge(x0,i,0,-rx-ry);
    		add_edge(i,x1,1e9,rx-ry);
    		add_edge(x1,i,0,-rx+ry);
    		add_edge(i,x2,1e9,-rx+ry);
    		add_edge(x2,i,0,rx-ry);
    		add_edge(i,x3,1e9,-rx-ry);
    		add_edge(x3,i,0,rx+ry);
    	}
    	rin(i,1,n){
    		int bx=read(),by=read(),bc=read();
    		add_edge(n+i,t,bc,0);
    		add_edge(t,n+i,0,0);
    		add_edge(x0,n+i,1e9,-bx-by);
    		add_edge(n+i,x0,0,bx+by);
    		add_edge(x1,n+i,1e9,-bx+by);
    		add_edge(n+i,x1,0,bx-by);
    		add_edge(x2,n+i,1e9,bx-by);
    		add_edge(n+i,x2,0,-bx+by);
    		add_edge(x3,n+i,1e9,bx+by);
    		add_edge(n+i,x3,0,-bx-by);
    	}
    	ek();
    	printf("%lld
    ",-mincost);
    	return 0;
    }
    
  • 相关阅读:
    2.1 JavaScript应用开发实践指南
    2 JavaScript应用开发实践指南
    一 JavaScript应用开发实践指南
    工作“触雷”经历与总结--记博弈论的应用
    设计模式之 简单工厂,工厂方法,抽象工厂
    C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是TAP(Task-based Asynchronous Pattern, 基于任务的异步模式)
    C语言知识结构
    Visual Studio 项目和解决方案 路径修改(解决部分模板丢失的问题)
    C#静态方法和实例方法的内存分配测试
    Java字符串String
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10981431.html
Copyright © 2011-2022 走看看