zoukankan      html  css  js  c++  java
  • FZOJ 4112 脱单计划

    这是一道费用流的好题。

    首先题目给你的男士女士以及之间的关系可以看做一个二分图。考虑暴力连边(用([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;
    }
    
    由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!
  • 相关阅读:
    kubectl 命令详解
    codeforce344 C report
    poj3041 建图+最小顶点覆盖(最大匹配数)
    poj1637 混合欧拉回路的判定
    poj1149 最大流好题 难在建图 好题
    targan 算法模板
    poj2186 强连通分量 targan算法的应用
    poj2723 2分 + 2-sat
    poj3061 尺取法
    poj3207 2-sat基础题
  • 原文地址:https://www.cnblogs.com/With-penguin/p/12720312.html
Copyright © 2011-2022 走看看