[最小割] Bzoj 3894 文理分科
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
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
题解
- 假设割一条s->i的边表示i选文科(称i为文科节点)
- 那么假如一个集合内的人都选文会获得一个满意度,建立一个新节点连到s流量为满意度,在新节点和每个理科节点连上一条流量为正无穷的边
- 只要集合内的任意一点选文科则必须要割掉这条边
- 理科同理
代码
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <algorithm> 5 #define inf 1000000000 6 using namespace std; 7 int ans,tot,n,m,T,cnt=1,Q[30005],head[30005],state[30005]; 8 int dx[5]={0,0,1,-1,0},dy[5]={1,-1,0,0,0}; 9 struct edge{ int to,from,v; }e[1000005]; 10 void insert(int x,int y,int v) 11 { 12 e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt,e[cnt].v=v; 13 e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt,e[cnt].v=0; 14 } 15 bool bfs() 16 { 17 int p=0,q=1; 18 memset(state,-1,sizeof(state)); 19 state[0]=Q[0]=0; 20 while (p!=q) 21 { 22 int u=Q[p]; p++; 23 for (int i=head[u];i;i=e[i].from) if (e[i].v&&state[e[i].to]==-1) state[e[i].to]=state[u]+1,Q[q++]=e[i].to; 24 } 25 return state[T]!=-1; 26 } 27 int dfs(int x,int maxf) 28 { 29 if (x==T) return maxf; 30 int f=0,r; 31 for (int i=head[x];i;i=e[i].from) 32 if (e[i].v&&state[e[i].to]==state[x]+1) 33 { 34 r=dfs(e[i].to,min(e[i].v,maxf-f)),e[i].v-=r,e[i^1].v+=r,f+=r; 35 if (f==maxf) return f; 36 } 37 if (!f) state[x]=-1; 38 return f; 39 } 40 int main() 41 { 42 scanf("%d%d",&n,&m),T=3*n*m+1; 43 for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&x),insert(0,(i-1)*m+j,x),tot+=x; 44 for (int i=1,x;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&x),insert((i-1)*m+j,T,x),tot+=x; 45 for (int i=1,v;i<=n;i++) 46 for (int j=1;j<=m;j++) 47 { 48 scanf("%d",&v),tot+=v; 49 for (int k=0;k<5;k++) 50 { 51 int x=dx[k]+i,y=dy[k]+j; 52 if (x>n||y>m||x<1||y<1) continue; 53 insert((i-1)*m+j+n*m,(x-1)*m+y,inf); 54 } 55 insert(0,(i-1)*m+j+n*m,v); 56 } 57 for (int i=1,v;i<=n;i++) 58 for (int j=1;j<=m;j++) 59 { 60 scanf("%d",&v),tot+=v; 61 for (int k=0;k<5;k++) 62 { 63 int x=dx[k]+i,y=dy[k]+j; 64 if (x>n||y>m||x<1||y<1) continue; 65 insert((x-1)*m+y,(i-1)*m+j+2*n*m,inf); 66 } 67 insert((i-1)*m+j+2*n*m,T,v); 68 } 69 while (bfs()) ans+=dfs(0,inf); 70 printf("%d",tot-ans); 71 }