BZOJ 3894:文理分科

Portal:http://www.lydsy.com/JudgeOnline/problem.php?id=3894

解析:

套路太深。

这种问题从最小割的思想方法出发(网络流不过是实现最小割的一种工具罢了)。

  • 割:表示对应选择的作出或否定
  • 边(容量INF):纯粹的连接边(即任何时候都不会被选为割的边)
  • 边(容量有限):表示某种限制
  • 源、汇点:具有一类相同性质的点的集合(类似二分图)

那么我们再看这道题。

割表示不选对应决策。

S向每个人连边,容量为学文的愉♂悦值,每个人向T连边,容量为学理的愉♂悦值

对于每个人再新建一个结点,表示这个人和周围四个人都学文,S向它连边,容量为Extra的愉♂悦值,这个结点向对应五个人连边,容量为INF,这样如果五个任意一个人学理,都会使Extra的边成为割,学理同理。

最后把所有输入值求和减最小割既是答案。

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())

using namespace std;

typedef long long LL;

const int maxn=100+23,maxm=maxn*maxn*16+23;
const int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
const int INF=(1<<30);

struct Edge{
 int u,v,cap,flow,next;
} E[maxm*2];

int n,m,ans,size,s,t,Esize,k,x,y,same_a,same_s,mini,flow,L,R;
int ind[maxn][maxn],art[maxn][maxn],sci[maxn][maxn],same_art[maxn][maxn],same_sci[maxn][maxn];
int F[maxm],last[maxm],num[maxm],cur[maxm],d[maxm],Q[maxm];

bool vis[maxm],mark;

void addedge(int x,int y,int cap)
{
 E[++Esize]=(Edge){x,y,cap,0,last[x]},last[x]=Esize;
 E[++Esize]=(Edge){y,x,0,0,last[y]},last[y]=Esize;
}

void BFS()
{
 L=R=1,Q[1]=t,d[t]=0;

 memset(vis,0,sizeof(vis)),vis[t]=1;

 while (L<=R)
 {
  x=Q[L],L++;

  for (int i=last[x];i;i=E[i].next)
   if (!vis[E[i].v]) vis[E[i].v]=1,d[E[i].v]=d[x]+1,Q[++R]=E[i].v;
 }
}

int calc()
{
 k=INF,x=t;
 while (x!=s) k=min(k,E[F[x]].cap-E[F[x]].flow),x=E[F[x]].u;

 x=t;
 while (x!=s) E[F[x]].flow+=k,E[F[x]^1].flow-=k,x=E[F[x]].u;

 return k;
}

int Maxflow()
{
 flow=0,BFS();

 memset(num,0,sizeof(num));
 rep(i,0,size) num[d[i]]++;

 rep(i,0,size) cur[i]=last[i];
 x=s;

 while (d[s]<size)
 {
  if (x==t) flow+=calc(),x=s;

  mark=0;
  for (int i=cur[x];i;i=E[i].next)
   if ((E[i].cap>E[i].flow)&&(d[x]==d[E[i].v]+1))
   {
    mark=1;
    cur[x]=i,x=E[i].v,F[x]=i;
    break;
   }

  if (!mark)
  {
   mini=size-1;
   for (int i=last[x];i;i=E[i].next)
    if (E[i].cap>E[i].flow) mini=min(mini,d[E[i].v]);
   
   if ((--num[d[x]])==0) break;
   d[x]=mini+1,num[d[x]]++;

   cur[x]=last[x];
   if (x!=s) x=E[F[x]].u;
  }
 }
 return flow;
}


int main()
{
 scanf("%d%d",&n,&m);

 ans=0;
 rep(i,1,n) rep(j,1,m) scanf("%d",&art[i][j]),ans+=art[i][j];
 rep(i,1,n) rep(j,1,m) scanf("%d",&sci[i][j]),ans+=sci[i][j];
 rep(i,1,n) rep(j,1,m) scanf("%d",&same_art[i][j]),ans+=same_art[i][j];
 rep(i,1,n) rep(j,1,m) scanf("%d",&same_sci[i][j]),ans+=same_sci[i][j];

 rep(i,1,n) rep(j,1,m) ind[i][j]=(i-1)*m+j;
 
 s=0,t=n*m+1,Esize=1;
 rep(i,1,n) rep(j,1,m) addedge(s,ind[i][j],art[i][j]),addedge(ind[i][j],t,sci[i][j]);

 size=t;

 rep(i,1,n)
  rep(j,1,m)
  {
   same_a=++size,same_s=++size;
   addedge(s,same_a,same_art[i][j]),addedge(same_s,t,same_sci[i][j]);
   addedge(same_a,ind[i][j],INF),addedge(ind[i][j],same_s,INF);

   rep(dic,0,3)
   {
    x=i+dx[dic],y=j+dy[dic];
    if ((1<=x)&&(x<=n)&&(1<=y)&&(y<=m))
     addedge(same_a,ind[x][y],INF),addedge(ind[x][y],same_s,INF);      
   }
  }
 
 printf("%d\n",ans-Maxflow());

 return 0;
}

posted @ 2017-01-04 21:33  Krew  阅读(115)  评论(0编辑  收藏  举报