【bzoj3894】文理分科 网络流最小割
原文地址:http://www.cnblogs.com/GXZlegend
题目描述
文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过)
小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想知道,大家应该如何选择,才能使所有人的满意值之和最大。请告诉他这个最大值。
输入
第一行为两个正整数:n,m
接下来n术m个整数,表示art[i][j];
接下来n术m个整数.表示science[i][j];
接下来n术m个整数,表示same_art[i][j];
输出
输出为一个整数,表示最大的满意值之和
样例输入
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
样例输出
152
题解
网络流最小割,和 bzoj3438 差不多。
具体做法:
1.S向每个学生连边,容量为理科收益;每个学生向T连边,容量为文科收益。
2.将每个学生组合拆成两个,S与第一个连边,容量为全理科收益,第一个向组合中学生连边,容量为inf;
组合中学生向第二个连边,容量为inf,第二个向T连边,容量为全文科收益。
3.跑最小割,答案为总收益-mincut。
#include <cstdio> #include <cstring> #include <queue> #define inf 0x3fffffff using namespace std; queue<int> q; int head[40000] , to[300000] , val[300000] , next[300000] , cnt = 1 , s , t , dis[40000]; void add(int x , int y , int z) { to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt; to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt; } bool bfs() { int x , i; while(!q.empty()) q.pop(); memset(dis , 0 , sizeof(dis)); dis[s] = 1 , q.push(s); while(!q.empty()) { x = q.front() , q.pop(); for(i = head[x] ; i ; i = next[i]) { if(val[i] && !dis[to[i]]) { dis[to[i]] = dis[x] + 1; if(to[i] == t) return 1; q.push(to[i]); } } } return 0; } int dinic(int x , int low) { if(x == t) return low; int temp = low , i , k; for(i = head[x] ; i ; i = next[i]) { if(val[i] && dis[to[i]] == dis[x] + 1) { k = dinic(to[i] , min(temp , val[i])); if(!k) dis[to[i]] = 0; val[i] -= k , val[i ^ 1] += k; if(!(temp -= k)) break; } } return low - temp; } int main() { int n , m , i , x , ans = 0; scanf("%d%d" , &n , &m); s = 0 , t = 3 * n * m + 1; for(i = 1 ; i <= n * m ; i ++ ) scanf("%d" , &x) , add(s , i , x) , ans += x; for(i = 1 ; i <= n * m ; i ++ ) scanf("%d" , &x) , add(i , t , x) , ans += x; for(i = 1 ; i <= n * m ; i ++ ) { scanf("%d" , &x) , add(s , i + n * m , x) , add(i + n * m , i , inf) , ans += x; if(i % m != 0) add(i + n * m , i + 1 , inf); if(i % m != 1) add(i + n * m , i - 1 , inf); if(i > m) add(i + n * m , i - m , inf); if(i <= (n - 1) * m) add(i + n * m , i + m , inf); } for(i = 1 ; i <= n * m ; i ++ ) { scanf("%d" , &x) , add(i + 2 * n * m , t , x) , add(i , i + 2 * n * m , inf) , ans += x; if(i % m != 0) add(i + 1 , i + 2 * n * m , inf); if(i % m != 1) add(i - 1 , i + 2 * n * m , inf); if(i > m) add(i - m , i + 2 * n * m , inf); if(i <= (n - 1) * m) add(i + m , i + 2 * n * m , inf); } while(bfs()) ans -= dinic(s , inf); printf("%d\n" , ans); return 0; }