题意描述:
给你一个 (n imes n) 的矩阵 (B) 和一个 (1 imes n) 的矩阵 (C) , 让你求一个 (1 imes n) 的 (01) 矩阵 (A), 使得: (D = (A imes B - C) imes A^T) 的值最大,输出这个最大值。
注: (A^T) 即 (A) 的转置,也就是将 (A) 的行和列交换后得到的矩阵。
数据范围: (nleq 500) .
solution:
乍一看,可能一点思路都没有。
我们先把求和的式子展开:
[(A imes B-C) imes A^T\
= A imes B imes A^T - C imes A^T\
= displaystylesum_{i=1}^{n}sum_{j=1}^{n} A_iA_jB_{ij} - sum_{i=1}^{n} C_iA_i
]
然后你会发现,当 (A_i = 1) 的代价为 (C_i) 当 (A_i) 和 (A_j) 同为 (1) 的时候收益为 (B_{ij}) .
这就是一个最大权闭合子图的模型。
建图:
- 对于每一个 (b_{ij}), 新建一个点 (u) ,表示这一组合。由 (s) 向 (u) 连一条容量为 (b_{ij}) 的边,由 (u) 分别向右侧的· (i) 和 (j) 两个点连一条容量为 (inf) 的边。
- 由右侧的点 (i) 向汇点连一条容量为 (c_i) 的边
最后 (b_{ij}) 之和减去最小割就是答案。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int inf = 1e7+10;
const int N = 1e6+10;
int n,cnt,sum,ans,s,t,tot = 1;
int head[N],dep[N],c[N],b[1010][1010],id[1010][1010];
struct node
{
int to,net,w;
}e[N<<1];
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
void add(int x,int y,int w)
{
e[++tot].to = y;
e[tot].w = w;
e[tot].net = head[x];
head[x] = tot;
}
bool bfs()
{
queue<int> q;
for(int i = 0; i <= t; i++) dep[i] = 0;
q.push(s); dep[s] = 1;
while(!q.empty())
{
int x = q.front(); q.pop();
for(int i = head[x]; i; i = e[i].net)
{
int to = e[i].to;
if(e[i].w && !dep[to])
{
dep[to] = dep[x] + 1;
q.push(to);
if(to == t) return 1;
}
}
}
return 0;
}
int dinic(int x,int flow)
{
if(x == t || !flow) return flow;
int rest = flow, val = 0;
for(int i = head[x]; i && rest; i = e[i].net)
{
int to = e[i].to;
if(!e[i].w || dep[to] != dep[x] + 1) continue;
val = dinic(to,min(rest,e[i].w));
if(val == 0) dep[to] = 0;
e[i].w -= val, e[i^1].w += val, rest -= val;
}
return flow - rest;
}
int main()
{
n = read();
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
b[i][j] = read();
id[i][j] = ++cnt;
}
}
for(int i = 1; i <= n; i++) c[i] = read();
s = 0, t = cnt + n + 1;
for(int i = 1; i <= n; i++)//建图
{
for(int j = 1; j <= n; j++)
{
sum += b[i][j];
add(s,id[i][j],b[i][j]), add(id[i][j],s,0);
add(id[i][j],cnt+i,inf), add(cnt+i,id[i][j],0);
add(id[i][j],cnt+j,inf), add(cnt+j,id[i][j],0);
}
}
for(int i = 1; i <= n; i++) add(cnt+i,t,c[i]), add(t,cnt+i,0);
int flow = 0;
while(bfs())
{
while(flow = dinic(s,inf)) ans += flow;
}
printf("%d
",sum-ans);
return 0;
}