文理分科

文理分科

题解

多简单的一道网络流呀

首先考虑如何在没有组合的情况下得到最大值。
很明显,如果直接最大流的话明显是不行的。至少我没有想出来。
考虑最大流最小割定理,我们可以通过最大流最小割来对原问题进行转换。
我们可以对于每一个学生向起点连一条流量为 a r t i art_{i} arti的边,向终点连一条为 s c i e n c e i science_{i} sciencei的边。
对于我们的最小割,一定会对于每个学生,都会在它与起点和终点的边中选择一条切断,而最小割的值就是价值最小的方案,由于是二选一,我们只需要用总价值减去最小价值,得到的就是最大价值了。
而最小割是等于最大流的,只需要跑一遍dinic就可以了。

但如果要加上组合的限制又应该怎么处理呢?
我们可以先建一些虚点表示每种组合,从起点或终点向每种组合连一条为该种组合价值的边,再从这个点连向所有属于它的点的为inf的边。
容易发现,当且仅当只有它所涵盖的人符合这个组合时它才有可能不会被切,否则它一定会被切掉的。当然,为inf的边是一定不会被切的。
所以,只需要通过这种方式把图建出来跑dinic即可。

注意,本题的dinic不能直接在bfs中记录下路径再来跑流量,必须用dfs来增广路径,因为每次只增广一条路的话会T飞的。
总时间复杂度为 O ( n 3 m 3 ) O\left(n^3m^3\right) O(n3m3),但很明显,是跑不满的。

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 30005
#define MAXM 500005
#define reg register
typedef long long LL;
const int INF=0x7f7f7f7f;
const LL inf=0x7f7f7f7f7f7f;
template<typename _T>
inline void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,head[MAXN],tot,pre[MAXN],pw[MAXN],cnt;
int dx[6]={0,1,0,-1,0},dy[6]={1,0,-1,0,0};
queue<int> q;int dep[MAXN],S,T;
struct edge{int to,nxt,flow,op;}e[MAXM];
inline void addEdge(int u,int v,int w){e[++tot]=(edge){v,head[u],w};head[u]=tot;}
inline void addedge(int u,int v,int w){addEdge(u,v,w);e[tot].op=tot+1;addEdge(v,u,0);e[tot].op=tot-1;}
bool bfs(){
	while(!q.empty())q.pop();
	for(reg int i=1;i<=cnt;++i)dep[i]=pre[i]=pw[i]=0;
	q.push(S);dep[S]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(reg int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(!dep[v]&&e[i].flow){
				q.push(v);dep[v]=dep[u]+1;pre[v]=u;
				pw[v]=i;if(v==T)return 1;
			}
		}
	}
	return 0;
}
int dfs(int u,int maxf){
	if(u==T||!maxf)return maxf;int res=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(!e[i].flow||dep[v]!=dep[u]+1)continue;
		int f=dfs(v,min(e[i].flow,maxf));
		e[i].flow-=f;e[e[i].op].flow+=f;res+=f;maxf-=f;
	}
	return res;
}
inline int sakura(){
	int res=0;
	while(bfs())res+=dfs(S,INF);
	return res;
}
inline int Id(const int x,const int y){return (x-1)*m+y;}
signed main(){
	read(n);read(m);int sum=0;S=3*n*m+1,T=cnt=3*n*m+2;
	for(reg int i=1;i<=n;++i)
		for(reg int j=1,f;j<=m;++j)
			read(f),addedge(S,Id(i,j),f),sum+=f;
	for(reg int i=1;i<=n;i++)
		for(reg int j=1,f;j<=m;++j)
			read(f),addedge(Id(i,j),T,f),sum+=f;
	for(reg int i=1;i<=n;++i)
		for(reg int j=1,f;j<=m;++j){
			read(f);addedge(S,Id(i,j)+n*m,f);sum+=f;
			for(reg int k=0;k<5;++k){
				int tx=i+dx[k],ty=j+dy[k];
				if(tx<1||tx>n||ty<1||ty>m)continue;
				addedge(Id(i,j)+n*m,Id(tx,ty),INF);
			}
		}
	for(reg int i=1;i<=n;++i)
			for(reg int j=1,f;j<=m;++j){
				read(f);addedge(Id(i,j)+2*n*m,T,f);sum+=f;
				for(reg int k=0;k<5;++k){
					int tx=i+dx[k],ty=j+dy[k];
					if(tx<1||tx>n||ty<1||ty>m)continue;
					addedge(Id(tx,ty),Id(i,j)+2*n*m,INF);
				}
			}
	int ans=sum-sakura();printf("%d\n",ans);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值