题意:给出n×n的矩阵B,1×n的矩阵C,求出一个只含0 1的1×n的矩阵A,使得D=( A×B-C ) × AT最大,输出D。
思路:
首先我们可以推出公式
继续整理:
从上式可以看出只要使得括号里的值最小,D就得到最大值。我们可以转化为最小割问题,因为ai只能取1,0。我们将ai=1的划分到S集合中,ai = 0划分到T集合中,我们先对所有的ai都与源点S连边,与汇点T也连边。
这样如果我们割掉的是S与ai的边,就说明ai = 0,则贡献的值是∑bij,如果割掉的是T与ai的边,就说明ai = 1,则贡献的值为ci,现在我们来看中间的那一项,如果割了ai与aj的边就说明ai = 1, aj = 0, 贡献的值是bij。贡献的值分别是边的流量,这样构造出来的最小割就是我们满足题意的括号中的值,最后用∑∑bij-最小割就是答案,这也是我们经典的项目分配问题。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1005
#define M 2040007
#define inf 0x7f7f7f7f
struct E
{
int v, ne, f;
E(){}
E(int _v, int _ne, int _f):v(_v),ne(_ne),f(_f){}
}e[M];
int head[N], size, out[N], lev[N], q[N];
void init()
{
memset(head, -1, sizeof(head));
size = 0;
}
void add(int u, int v, int f)
{
e[size] = E(v, head[u], f);
head[u] = size++;
e[size] = E(u, head[v], 0);
head[v] = size++;
}
int bfs(int S,int T)
{
int u, rear = 0, v;
memset(lev, -1, sizeof(lev));
lev[S] = 0, q[rear ++] = S;
for(int i = 0; i < rear; i ++) {
u = q[i];
for(int j = head[u]; j != -1; j = e[j].ne) {
v = e[j].v;
if(e[j].f && lev[v] == -1)
{
lev[v] = lev[u] + 1, q[rear ++] = v;
if(v == T) return 1;
}
}
}
return 0;
}
int dfs(int cur, int a, int T)
{
if(cur == T)
return a;
int v, f;
for(int &i = out[cur]; i != -1; i = e[i].ne) {
v = e[i].v, f = e[i].f;
if(f && lev[v] == lev[cur] + 1)
{
int t = dfs(v, min(a, f), T);
if(t)
{
e[i].f -= t, e[i ^ 1].f += t;
return t;
}
}
}
return 0;
}
int dinic(int S,int T)
{
int ans = 0;
while(bfs(S,T))
{
memcpy(out, head, sizeof(head));
while(int t = dfs(S, inf, T))
ans += t;
}
return ans;
}
int b[N][N], c[N], sb[N];
int main() {
int T, n, i, j, sum;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
sum = 0;
for (i = 1;i <= n;i++) {
for (j = 1;j <= n;j++) {
scanf("%d", &b[i][j]);
sum += b[i][j];
}
}
for (i = 1;i <= n;i++) {
sb[i] = 0;
for (j = 1;j <= n;j++) sb[i] += b[j][i];
}
for (i = 1;i <= n;i++) scanf("%d", &c[i]);
init();
for (i = 1;i <= n;i++) {
add(0, i, sb[i]), add(i, n+1, c[i]);
for (j = 1;j <= n;j++) {
if (i == j) continue;
add(i, j, b[j][i]);
}
}
printf("%d\n", sum-dinic(0, n+1));
}
}