BZOJ3894:文理分科

3894: 文理分科

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 1076  Solved: 630
[Submit][Status][Discuss]

Description

 文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠
结过)
 小P所在的班级要进行文理分科。他的班级可以用一个n*m的矩阵进行
描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择
一科。同学们在选择科目的时候会获得一个满意值。满意值按如下的方式
得到:
1.如果第i行第秒J的同学选择了文科,则他将获得art[i][j]的满意值,如
  果选择理科,将得到science[i][j]的满意值。
2.如果第i行第J列的同学选择了文科,并且他相邻(两个格子相邻当且
  仅当它们拥有一条相同的边)的同学全部选择了文科,则他会更开
  心,所以会增加same_art[i][j]的满意值。
3.如果第i行第j列的同学选择了理科,并且他相邻的同学全部选择了理
  科,则增加same_science[i]j[]的满意值。
  小P想知道,大家应该如何选择,才能使所有人的满意值之和最大。请
告诉他这个最大值。
 

Input

 
第一行为两个正整数:n,m
接下来n术m个整数,表示art[i][j];
接下来n术m个整数.表示science[i][j];
接下来n术m个整数,表示same_art[i][j];
 

Output

 
输出为一个整数,表示最大的满意值之和
 

Sample Input

3 4
13 2 4 13
7 13 8 12
18 17 0 5

8 13 15 4
11 3 8 11
11 18 6 5

1 2 3 4
4 2 3 2
3 1 0 4

3 2 3 2
0 2 2 1
0 2 4 4

Sample Output

152

HINT

样例说明

1表示选择文科,0表示选择理科,方案如下:

1  0  0  1

0  1  0  0

1  0  0  0

 

N,M<=100,读入数据均<=500

思路{

  首先应当想到这是一道最小割的题。

  如何连边呢?我们想,

  无论怎么样即使连一条Inf的边它也不会是最小割(因为有其他边的限制);

  要保证最小割能分成两个点集,且保证决策的合理性,还要考虑周围的人的影响。

  所以我们不难想到{

     设源点S所在的点集为文科,汇点T所在点集为理科.

     那么连S到点i的容量为文科费用,i到T的容量为理科费用。

     好的,接下来是最重要的一部分。

     一个点和周围4个点有共同贡献,所以考虑新建一个点;

     与这5个点连Inf的边,贡献的话,

     由于是割,最后的答案为总权值减最小割,那么

     文科贡献连S∈割集就对应当前这些点不属于文科,理科同理。

     否则的话连出来无意义。。。。

  }还是比较水的,还有一道一毛一样的题BZOJ2127happiness

  网络流好久没敲了,调了好久。。。。。。。。

}

#include<bits/stdc++.h>
#define N 101*101
#define Inf (1<<20)
#define pos(i,j) ((i-1)*m+j)
using namespace std;
struct ed{int nxt,to,c;}e[N*100];
int head[N*10],tot,deep[N*10],n,m;

void add(int u,int v,int c){e[tot].nxt=head[u];e[tot].to=v;e[tot].c=c;head[u]=tot++;}
void ADD(int u,int v,int c){add(u,v,c),add(v,u,0);}

bool BFS(int s,int t){queue<int>que;while(!que.empty())que.pop();
  memset(deep,0,sizeof(deep));deep[s]=1;
  que.push(s);while(!que.empty()){
    int u=que.front();que.pop();
    for(int i=head[u];i!=-1;i=e[i].nxt)if(e[i].c&&(!deep[e[i].to])){
	int v=e[i].to;deep[v]=deep[u]+1;
	que.push(v);
	if(v==t)return true;
      }
  }return false;
}
int dinic(int s,int t,int T){
  if(s==t)return T;int tag(0);
  for(int i=head[s];i!=-1;i=e[i].nxt)if(deep[e[i].to]==deep[s]+1&&e[i].c){
      int v=e[i].to;int d=dinic(v,t,min(T-tag,e[i].c));
      e[i].c-=d,e[i^1].c+=d;tag+=d;
      if(tag==T)break;
    }if(!tag)deep[s]=0;return tag;
}
int maxflow(int s,int t){int flow=0;
  while(BFS(s,t))flow+=dinic(s,t,Inf);
  return flow;
}
int main(){
  freopen("1.in","r",stdin);
  //freopen("1.out","w",stdout);
  memset(head,-1,sizeof(head));
  int f,Ans(0);scanf("%d%d",&n,&m);
  for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
      scanf("%d",&f),ADD(0,pos(i,j),f),Ans+=f;
  for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
      scanf("%d",&f),ADD(pos(i,j),n*m+1,f),Ans+=f;
  int cnt=n*m+1;
  for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j){
      scanf("%d",&f);cnt++;
      if(i!=1)ADD(cnt,pos(i-1,j),Inf);
      if(i!=n)ADD(cnt,pos(i+1,j),Inf);
      if(j!=1)ADD(cnt,pos(i,j-1),Inf);
      if(j!=m)ADD(cnt,pos(i,j+1),Inf);
      ADD(cnt,pos(i,j),Inf);
      ADD(0,cnt,f),Ans+=f;
    }
  for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j){
      scanf("%d",&f);cnt++;
      if(i!=1)ADD(pos(i-1,j),cnt,Inf);
      if(i!=n)ADD(pos(i+1,j),cnt,Inf);
      if(j!=1)ADD(pos(i,j-1),cnt,Inf);
      if(j!=m)ADD(pos(i,j+1),cnt,Inf);
      ADD(pos(i,j),cnt,Inf);
      ADD(cnt,n*m+1,f),Ans+=f;
    }
  cout<<Ans-maxflow(0,n*m+1)<<"
";
  return 0;
}

  

原文地址:https://www.cnblogs.com/zzmmm/p/7212452.html