https://www.oj.swust.edu.cn/problem/show/2543
赛场布置
Time Limit:5000MS Memory Limit:131072KB
Description
上决╇ф成功地阻止了大魔王的复活,于是西南科技大学信息工程学院又举行了一年一度的院赛。为了让比赛能够顺利进行,上决╇ф让FM负责布置比赛的其中一个赛场,上决╇ф告诉FM报名参加的只有男生队和女生队两种,赛场可以看做一个N行M列的矩阵教室,每个位置上只可以容纳一支队伍(男生队或者女生队),现在上决╇ф通过某种交易得到了一些数据,这些数据包含的信息如下:
1.如果第i行第j列的位置坐的是男生队,那么此队将得到A[i][j]的战斗力。
2.如果第i行第j列的位置坐的是女生队,那么此队将得到B[i][j]的战斗力。
3.如果第i行第j列的队伍在相邻的位置中有x支异性队伍(相邻指的是两位置有公共边,显然x<=4),那么这个队伍能得到x*C[i][j]的额外战斗力加成,咦哟
4.如果第i行第j列的队伍在相邻的位置中有x支同性队伍(相邻指的是两位置有公共边,显然x<=4),那么这个队伍的战斗力会减少x*D[i][j]。
上决╇ф要求FM根据上面的数据,合理安排每个位置,使得整个赛场所有队伍的战斗力总和最大。上决╇ф只是让FM布置这一个赛场,所以保证了男生队和女生队数量足够。你能帮FM求出最大的战斗力总和吗?
Input
输入第一行为两个整数N和M,代表赛场大小。 接下来依次给出四个N行M列的矩阵A[i][j] , B[i][j] , C[i][j] , D[i][j] , 四个矩阵的数据代表的意义就是题面所述。 其中:1 <= N , M <= 100 , 0 <= A[i][j] , B[i][j] , C[i][j] , D[i][j] <= 500。
Output
输出一个整数,代表最大的战斗力。
Source 唐灿
一道最小割问题。
建图方法:
我们首先把整个矩阵黑白染色
新建一个源点S,汇点T
S到黑点的流量为坐男生队伍所得到的战斗力
黑点到T的流量为坐女生队伍所得到的战斗力
S到白点的流量为坐女生队伍所得到的战斗力
白点到T的流量为坐男生队伍所得到的战斗力
那么对于相邻的一个黑点( i , j )和一个白点( x , y )
Addedge(S , idx ( i , j ) , A[i][j] )
Addedge( idx ( i , j ) , T, B[i][j] )
Addedge( S , idx ( x , y ) , B[x][y] )
Addedge( idx ( x , y ) , T , A[x][y] )
Addedge( idx ( i , j ) , idx ( x , y ) , C[i][j]+C[x][y]+D[i][j]+D[x][y] )
Addedge( idx ( x , y ) , idx ( i , j ) , C[i][j]+C[x][y]+D[i][j]+D[x][y] )
那么答案=总的战斗力ans – maxflow()
其中ans不包含相邻位置坐同性队伍所扣除的战斗力,ans具体求法看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 10010;
const int maxm = 200010;
const int inf = 1e9 + 5;
const LL INF = (LL)inf * inf;
int s, t;
int num, p[maxn];
int cur[maxn], d[maxn], v[maxn];
struct edge
{
int u, v, cap, flow, next;
edge() {}
edge(int u, int v, int cap, int flow, int next): u(u), v(v), cap(cap), flow(flow),
next(next) {}
} E[maxm];
void init()
{
num = 0;
memset(p, -1, sizeof(p));
}
void add(int u, int v, int cap)
{
E[num] = (edge)
{
u, v, cap, 0, p[u]
};
p[u] = num++;
E[num] = (edge)
{
v, u, 0, 0, p[v]
};
p[v] = num++;
}
int q[maxn];///循环队列
bool bfs()
{
for(int i = 0; i <= t; i++) v[i] = 0;
v[s] = 1;
d[s] = 0;
int l, r;
l = 0, r = 1;
q[l] = s;
while(l != r)
{
int x = q[l++];
if(l >= maxn) l =0;
for(int i = p[x]; i != -1; i = E[i].next)
{
edge e = E[i];
if(!v[e.v] && e.cap > e.flow)
{
v[e.v] = 1;
d[e.v] = d[x] + 1;
q[r++] = e.v;
if(r >= maxn) r =0;
}
}
}
return v[t];
}
int dfs(int x, int a)
{
if(x == t || !a) return a;
int flow = 0;
for(int &i = cur[x]; i != -1; i = E[i].next)
{
edge &e = E[i];
if(d[e.v] != d[x] + 1) continue;
int f = dfs(e.v, min(a, e.cap - e.flow));
if(f)
{
e.flow += f;
E[i ^ 1].flow -= f;
flow += f;
a -= f;
if(!a) break;
}
}
return flow;
}
int dinic()
{
int flow = 0;
while(bfs())
{
for(int i = 0; i <= t; i++) cur[i] = p[i];
flow += dfs(s, inf);
}
return flow;
}
int A[110][110], B[110][110], C[110][110],D[110][110];
int id[110][110];
int f[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
int main()
{
int n, m;
while (scanf("%d%d", &n, &m) == 2)
{
int ans = 0, cnt = 0;
init();
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
id[i][j] = ++cnt;
s = 0, t = n * m + 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &A[i][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &B[i][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &C[i][j]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
scanf("%d", &D[i][j]);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if ((i + j) & 1)
{
add(s, id[i][j], A[i][j]);
add(id[i][j], t, B[i][j]);
}
else
{
add(s, id[i][j], B[i][j]);
add(id[i][j], t, A[i][j]);
}
ans += A[i][j] + B[i][j];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
for (int k = 0; k < 4; k++)
{
int mx = i + f[k][0];
int my = j + f[k][1];
if (mx >= 1 && mx <= n && my >= 1 && my <= m)
{
ans += C[i][j];
add(id[i][j], id[mx][my], C[i][j] + C[mx][my]+D[i][j]+D[mx][my]);
}
}
}
}
printf("%d\n", ans - dinic());
}
return 0;
}