zoukankan      html  css  js  c++  java
  • BZOJ 3894 Luogu P4313 文理分科 (最小割)

    题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=3894

    (luogu) https://www.luogu.org/problemnew/show/P4313

    题解:

    做法很简单,就是最小割,(S)集属于文科,(T)集属于理科,对于每个点(i), 起点(S)(i)(a_i)(文科收益/理科代价),(i)向终点(T)(b_i) (理科收益/文科代价),对于每一个点(i)再新建两点(i_a)(同文点)和(i_b)(同理点),(S)(i_a)连边(aa_i)(同文收益),(i_b)(T)(bb_i)(同理收益),中间对于(i)(i)座位相连的每个点,从(i_a)向该点连边,从该点向(i_b)连边,边权均为(+inf).

    我的错误做法: 如果同文同理建成同一个点,和座位相连的每个点连双向边,那么这是错的,如果连单向边也是错的。因为建两个点实际上可以保证如果(i_a)属于(S)集则它连向的人都选文,如果(i_b)属于(T)集则连向它的人都选理,如果它们与(S,T)之间的边都被割掉了,则它们对这些人没有任何限制,这些人仍是独立的。但如果同文同理建成同一个点连双向边,那么这些点之间构成强连通分量,相当于默认所有人必须在同一集合,这是最离谱的做法我居然能想出来。如果连单向边呢,比如从新点往这几个人连边,从(S)往新点连边,从新点往(T)连边,那么相当于规定“如果新点属于(S)则这些人全属于(S), 如果新点属于(T)则对这些人没有要求”。总之,从(i)(j)连边(inf)则相当于如果(iin S)(jin S), 但是如果(iin T)则对(j)没有要求;如果(jin T)(iin T),而如果(jin S)则没有要求对(i)没有要求(这两句话是等价的)。(i)(j)之间连双向(inf)边则相当于强制两点在同一集合中。

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cassert>
    using namespace std;
    
    const int INF = 1e8;
    
    namespace MaxFlow
    {
    	const int N = 3e4+2;
    	const int M = 14e4;
    	struct Edge
    	{
    		int v,w,nxt,rev;
    	} e[(M<<1)+3];
    	int fe[N+3];
    	int te[N+3];
    	int que[N+3];
    	int dep[N+3];
    	int n,en,s,t;
    	void addedge(int u,int v,int w)
    	{
    		en++; e[en].v = v; e[en].w = w;
    		e[en].nxt = fe[u]; fe[u] = en; e[en].rev = en+1;
    		en++; e[en].v = u; e[en].w = 0;
    		e[en].nxt = fe[v]; fe[v] = en; e[en].rev = en-1;
    	}
    	bool bfs()
    	{
    		for(int i=1; i<=n; i++) dep[i] = 0;
    		int head = 1,tail = 1; que[tail] = s; dep[s] = 1;
    		while(head<=tail)
    		{
    			int u = que[head]; head++;
    			for(int i=fe[u]; i; i=e[i].nxt)
    			{
    				if(dep[e[i].v]==0 && e[i].w>0)
    				{
    					dep[e[i].v] = dep[u]+1;
    					tail++; que[tail] = e[i].v;
    				}
    			}
    		}
    		return dep[t]!=0; 
    	}
    	int dfs(int u,int cur)
    	{
    		if(u==t) {return cur;}
    		int rst = cur;
    		for(int i=te[u]; i; i=e[i].nxt)
    		{
    			if(dep[e[i].v]==dep[u]+1 && e[i].w>0 && rst>0)
    			{
    				int flow = dfs(e[i].v,min(rst,e[i].w));
    				if(flow>0)
    				{
    					rst -= flow; e[i].w -= flow; e[e[i].rev].w += flow;
    					if(e[i].w>0) {te[u] = i;}
    					if(rst==0) return cur;
    				}
    			}
    		}
    		if(cur==rst) dep[u] = 0;
    		return cur-rst;
    	}
    	int dinic(int _n,int _s,int _t)
    	{
    		int ret = 0;
    		n = _n,s = _s,t = _t;
    		while(bfs())
    		{
    			for(int i=1; i<=n; i++) te[i] = fe[i];
    			ret += dfs(s,INF);
    		}
    		return ret;
    	}
    }
    using MaxFlow::addedge;
    using MaxFlow::dinic;
    
    const int N = 100;
    int a[N+3][N+3],b[N+3][N+3],aa[N+3][N+3],bb[N+3][N+3];
    int n,m;
    
    int getid(int x,int y) {return (x-1)*m+y+2;}
    
    int main()
    {
    	scanf("%d%d",&n,&m); int ans = 0;
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=1; j<=m; j++)
    		{
    			scanf("%d",&a[i][j]); ans += a[i][j];
    		}
    	}
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=1; j<=m; j++)
    		{
    			scanf("%d",&b[i][j]); ans += b[i][j];
    		}
    	}
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=1; j<=m; j++)
    		{
    			scanf("%d",&aa[i][j]); ans += aa[i][j];
    		}
    	}
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=1; j<=m; j++)
    		{
    			scanf("%d",&bb[i][j]); ans += bb[i][j];
    		}
    	}
    	for(int i=1; i<=n; i++)
    	{
    		for(int j=1; j<=m; j++)
    		{
    			int x = getid(i,j);
    			addedge(1,x,a[i][j]);
    			addedge(x,2,b[i][j]);
    			addedge(1,x+n*m,aa[i][j]);
    			addedge(x+n*m*2,2,bb[i][j]);
    			addedge(x+n*m,x,INF);
    			addedge(x,x+n*m*2,INF);
    			if(i>1)
    			{
    				addedge(x+n*m,getid(i-1,j),INF);
    				addedge(getid(i-1,j),x+n*m*2,INF);
    			}
    			if(j>1)
    			{
    				addedge(x+n*m,getid(i,j-1),INF);
    				addedge(getid(i,j-1),x+n*m*2,INF);
    			}
    			if(i<n)
    			{
    				addedge(x+n*m,getid(i+1,j),INF);
    				addedge(getid(i+1,j),x+n*m*2,INF);
    			}
    			if(j<m)
    			{
    				addedge(x+n*m,getid(i,j+1),INF);
    				addedge(getid(i,j+1),x+n*m*2,INF);
    			}
    		}
    	}
    	int tmp = dinic(n*m*3+2,1,2);
    	ans -= tmp;
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Java for LeetCode 229 Majority Element II
    Java for LeetCode 228 Summary Ranges
    Java for LeetCode 227 Basic Calculator II
    Java for LintCode 颜色分类
    Java for LintCode 链表插入排序
    Java for LintCode 颠倒整数
    Java for LintCode 验证二叉查找树
    Java for LeetCode 226 Invert Binary Tree
    Java for LeetCode 225 Implement Stack using Queues
    Java for LeetCode 224 Basic Calculator
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11079176.html
Copyright © 2011-2022 走看看