文理分科 (最小割问题)
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
非常經典的最小割問題
要么選理科,要么選文科
設計兩個超級點SSS表示選文科,TTT表示選理科
與SSS分在一起的則選的是文科得到artartart,否則是理科得到sciencesciencescience
考慮怎么判斷可額外增加的滿意值
不妨再新添點與SSS連流量same_art的邊
怎么保證選了新添點,綁在一起的人都一起選文科呢??
直接與綁在一起的上下左右中點都連流量infinfinf,則一定不會出現在最小割里面
新添點與TTT連流量same_science的邊
怎么保證選了新添點,綁在一起的人都一起選理科呢??
同樣的,直接與綁在一起的上下左右中點都連流量infinfinf
則一定不會出現在最小割里面
最后就在這個圖內跑最大流,所有的滿意值減去最小割就是真正的最大滿意值
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; }總結
以上是生活随笔為你收集整理的文理分科 (最小割问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓手机如何省电 省电方法介绍
- 下一篇: [HNOI2013]消毒 (匈牙利最大匹