文理分科
题解
多简单的一道网络流呀
首先考虑如何在没有组合的情况下得到最大值。
很明显,如果直接最大流的话明显是不行的。至少我没有想出来。
考虑最大流最小割定理,我们可以通过最大流最小割来对原问题进行转换。
我们可以对于每一个学生向起点连一条流量为
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;
}