首页 > 其他分享 >文理分科问题 网络流划分

文理分科问题 网络流划分

时间:2023-02-08 17:13:28浏览次数:44  
标签:分科 head int add tot 文理 划分 maxn inf

文理分科问题 网络流划分

题目大意

全班分科,每位同学分到文科能获得一定满意值,分到理科能获得一定满意值,如果上下左右的同学都被分到同一科也能增加一定的满意值,要求找到最优划分,使得满意值最大。

题解

对于这种把原集合一分为二的问题,容易想到它和最小割的形式类似,因为在最小割中所有点要么在s能到达的集合,要么在t联通的集合。

不考虑分组,我们可以把s连向所有节点,将边权设置成文科的满意值,同时将理科的满意值连向t。

加上分组问题就有点难办了,我们需要让一组人的收益增加当且仅当一组人选了一样的科目,这该怎么建图呢?

这就不得不说到一个建图时常用的技巧,无穷边:

一个显然的事实是,最小割一定不会包含无穷边,那么无穷边的作用就是强制两个点一荣俱荣,一损俱损保卫地球,有你有我,准确的说,如果无穷边的起点被包含在哪个点集,那么终点也在这个点集。(当然,本题中被割断后实际上起点不属于任何一个点集)

强制 s-u 之间在最小割中被割断或者 v-t 在最小割中被割断。

我们只需要对于每一组分配两个虚点,一个向s连一条表示选文,另一个向t连一条表示选理,再连向约束点表示条件就行了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int head[maxn],w[maxn],to[maxn],nxt[maxn],tot=1;
int s,t;
void add(int u,int v,int wi){
	nxt[++tot]=head[u],to[head[u]=tot]=v,w[tot]=wi;
	nxt[++tot]=head[v],to[head[v]=tot]=u,w[tot]=0;
}
int inf =0x3f3f3f3f;
int d[maxn];
int bfs(){
	memset(d,0,sizeof(d));
	queue<int>q;
	q.push(s);
	d[s]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=head[u];i;i=nxt[i]){
			int v=to[i];
			if(w[i]&&!d[v]){
				d[v]=d[u]+1;
				if(v==t)return 1;
				q.push(v)
			}
		}
	}
	return 0;
}
int dinic(int u,int flow){
	int rest=flow;
	if(u==t)return flow;
	for(int i=head[u];i&&rest;i=nxt[i]){
		if(w[i]&&d[to[i]]==d[u]+1){
			int x=dinic(to[i],min(rest,w[i]));
			if(!x)d[to[i]]=0;
			w[i]-=x;
			w[i^1]+=x;
			rest-=x;
		}
	}
	return flow-rest;
}
int main(){
	int n,m;
	cin>>n>>m;
	s=0,t=4*n*m+1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			int x;
			cin>>x;
			add(s,(i-1)*m+j,x);
			add((i-1)*m+j,(i-1)*m+j+n*m,inf);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			int x;
			cin>>x;
			add((i-1)*m+j+n*m,t,x);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			int x;
			cin>>x;
			add(s,(i-1)*m+j+2*n*m,x);
			if(i>1)add((i-1)*m+j+2*n*m,(i-2)*n+j,inf);
			if(j>1)add((i-1)*m+j+2*n*m,(i-1)*m+j-1,inf);
			if(i<n)add((i-1)*m+j+2*n*m,(i)*n+j,inf);
			if(j<m)add((i-1)*m+j+2*n*m,(i-1)*m+j+1,inf);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			int x;
			cin>>x;
			add((i-1)*m+j+3*n*m,t,x);
			if(i>1)add((i-1)*m+j+3*n*m,(i-2)*n+j+n*m,inf);
			if(j>1)add((i-1)*m+j+3*n*m,(i-1)*m+j-1+n*m,inf);
			if(i<n)add((i-1)*m+j+3*n*m,(i)*n+j+n*m,inf);
			if(j<m)add((i-1)*m+j+3*n*m,(i-1)*m+j+1+n*m,inf);
		}
	int maxflow=0;
	for(int i=2;i<=tot;i++){
		if(w[i])cout<<to[i^1]<<" "<<to[i]<<" "<<w[i]<<endl;
	}
	while(bfs()){
	
		while(int x=dinic(s,inf))maxflow+=x;
	}
	cout<<maxflow<<endl;
}

标签:分科,head,int,add,tot,文理,划分,maxn,inf
From: https://www.cnblogs.com/Hushizhi/p/17102525.html

相关文章