題意:給出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));
}
}