• 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;
    }
    
  • 相关阅读:
    ps命令
    关于typedef的用法总结
    C#中正则表达式的使用
    调试与编译
    大端和小端
    64位程序内存之我看
    C/C++内存泄漏及检测
    内核中的 likely() 与 unlikely()
    do/while(0) c4127
    django+xadmin在线教育平台(六)
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11079176.html
Copyright © 2020-2023  润新知