题目
每个同学都只能选择文科或者理科一种,选择文科会获得一个权值,理科也有,如果一个人以及周围四个人都选择了一个学科,那么又会获得一个权值。
将权值最大化。
分析
很经典的网络流题目,用最小割。
首先我们的模型想法是把最终和S集联通的弄成文集,T集联通的弄成理集,然后用最小割割掉,为了使用最小割,我们采取首先加入所有权值,然后看根据选择减少了哪些权值。
连边的方式就是:
对于一个结点,s->i连文科,i->t连理科。
然后弄一个全部文科的结点i’,s->i’流量是权值,i’->对应的五个点,流量inf。
理科的话自己推一下就出来了。
这样做的结果就是,一旦一个结点选择理科,那么文科边,以及它所有对应的全部选择文科的那种结点,全部会被割掉,使得获取正确答案。
代码
#include<cmath>
#include<queue>
#include<cctype>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10000*3+105,maxm=maxn*5,inf=1e9;
int np,first[maxn];
struct edge{
int from,to,next,cap,flow;
}E[maxm<<1];
void add(int u,int v,int c)
{
E[++np]=(edge){u,v,first[u],c,0};
first[u]=np;
E[++np]=(edge){v,u,first[v],0,0};
first[v]=np;
}
int dx[]={0,1,-1,0,0};
int dy[]={0,0,0,1,-1};
int n,m,s,t,nm,sum;
#define getId(x,y) ((x)-1)*m+(y)
void Init()
{
int a,id,id1,ni,nj;
np=-1;
memset(first,-1,sizeof(first));
scanf("%d%d",&n,&m);
nm=n*m,s=3*nm+1,t=s+1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a);
sum+=a;
id=getId(i,j);
add(s,id,a);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a);
sum+=a;
id=getId(i,j);
add(id,t,a);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a);
sum+=a;
id=getId(i,j)+nm;
add(s,id,a);
for(int k=0;k<5;k++)
{
ni=i+dx[k],nj=j+dy[k];
if(ni<1 || nj<1 || ni>n || nj>m)continue;
id1=getId(ni,nj);
add(id,id1,inf);
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a);
sum+=a;
id=getId(i,j)+2*nm;
add(id,t,a);
for(int k=0;k<5;k++)
{
ni=i+dx[k],nj=j+dy[k];
if(ni<1 || nj<1 || ni>n || nj>m)continue;
id1=getId(ni,nj);
add(id1,id,inf);
}
}
}
}
int dist[maxn],gap[maxn];
int SAP(int i,int lim)
{
if(i==t)return lim;
int flow=0,tmp;
for(int p=first[i];p!=-1;p=E[p].next)if(E[p].cap-E[p].flow)
{
int j=E[p].to;
if(dist[i]==dist[j]+1)
{
tmp=SAP(j,min(lim-flow,E[p].cap-E[p].flow));
E[p].flow+=tmp;
E[p^1].flow-=tmp;
flow+=tmp;
if(dist[s]>=t || lim==flow)return flow;
}
}
if(flow==0)
{
if(--gap[dist[i]]==0)dist[s]=t;
gap[++dist[i]]++;
}
return flow;
}
void maxFlow()
{
memset(gap,0,sizeof(gap));
memset(dist,0,sizeof(dist));
gap[0]=t;
int ret=0;
while(dist[s]<t)ret+=SAP(s,inf);
printf("%d\n",sum-ret);
}
int main()
{
//freopen("in.txt","r",stdin);
Init();
maxFlow();
return 0;
}