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
solution
非常经典的最小割问题
要么选理科,要么选文科
设计两个超级点 S S S表示选文科, T T T表示选理科
与 S S S分在一起的则选的是文科得到 a r t art art,否则是理科得到 s c i e n c e science science
考虑怎么判断可额外增加的满意值
不妨再新添点与 S S S连流量same_art的边
怎么保证选了新添点,绑在一起的人都一起选文科呢??
直接与绑在一起的上下左右中点都连流量 i n f inf inf,则一定不会出现在最小割里面
新添点与 T T T连流量same_science的边
怎么保证选了新添点,绑在一起的人都一起选理科呢??
同样的,直接与绑在一起的上下左右中点都连流量 i n f inf inf
则一定不会出现在最小割里面
最后就在这个图内跑最大流,所有的满意值减去最小割就是真正的最大满意值
code
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 200005
#define inf 0x7f7f7f7f
struct node {
int nxt, to, flow;
}edge[maxn << 1];
queue < int > q;
int n, m, cnt = 1, num;
int dep[maxn], head[maxn], cur[maxn];
int dx[4] = {
1, -1, 0, 0 }, dy[4] = {
0, 0, 1, -1 };
int id( int x, int y ) {
return ( x - 1 ) * m + y;
}
bool inside( int x, int y ) {
if( x < 1 || x > n || y < 1 || y > m ) return 0;
else return 1;
}
void addEdge( int u, int v, int w ) {
cnt ++;
edge[cnt].nxt = head[u];
edge[cnt].to = v;
edge[cnt].flow = w;
head[u] = cnt;
cnt ++;
edge[cnt].nxt = head[v];
edge[cnt].to = u;
edge[cnt].flow = 0;
head[v] = cnt;
}
int bfs( int s, int t ) {
memcpy( cur, head, sizeof( head ) );
memset( dep, 0, sizeof( dep ) );
dep[s] = 1;
q.push( s );
while( ! q.empty() ) {
int u = q.front(); q.pop();
for( int i = head[u];i;i = edge[i].nxt ) {
int v = edge[i].to;
if( ! dep[v] && edge[i].flow ) {
dep[v] = dep[u] + 1;
q.push( v );
}
}
}
return dep[t];
}
int dfs( int u, int t, int cap ) {
if( ! cap || u == t ) return cap;
int flow = 0;
for( int i = cur[u];i;i = edge[i].nxt ) {
cur[u] = i;
int v = edge[i].to;
if( dep[v] == dep[u] + 1 ) {
int w = dfs( v, t, min( cap, edge[i].flow ) );
if( ! w ) continue;
flow += w;
cap -= w;
edge[i].flow -= w;
edge[i ^ 1].flow += w;
if( ! cap ) break;
}
}
return flow;
}
int dinic( int s, int t ) {
int ans = 0;
while( bfs( s, t ) )
ans += dfs( s, t, inf );
return ans;
}
int main() {
scanf( "%d %d", &n, &m );
int ans = 0, S = 0, T = n * m + 1, w; num = n * m + 1;
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= m;j ++ ) {
scanf( "%d", &w );
addEdge( S, id( i, j ), w );
ans += w;
}
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= m;j ++ ) {
scanf( "%d", &w );
addEdge( id( i, j ), T, w );
ans += w;
}
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= m;j ++ ) {
scanf( "%d", &w );
ans += w;
num ++;
addEdge( S, num, w );
addEdge( num, id( i, j ), inf );
for( int k = 0;k < 4;k ++ )
if( inside( i + dx[k], j + dy[k] ) )
addEdge( num, id( i + dx[k], j + dy[k] ), inf );
}
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= m;j ++ ) {
scanf( "%d", &w );
ans += w;
num ++;
addEdge( num, T, w );
addEdge( id( i, j ), num, inf );
for( int k = 0;k < 4;k ++ )
if( inside( i + dx[k], j + dy[k] ) )
addEdge( id( i + dx[k], j + dy[k] ), num, inf );
}
printf( "%d\n", ans - dinic( S, T ) );
return 0;
}