Problem
【TJOI2015】线性代数
TimeLimit:10Sec
MemoryLimit:128MB
Description
给出一个N×N的矩阵B和一个1×N的矩阵C。
求出一个1×N的01矩阵A,使得D=(A×B−C)×AT最大,输出最大的D值。
第一行输入一个整数N。
接下来N行输入B矩阵,第i行第j个数字代表Bi,j.
接下来一行输入N个整数,代表矩阵C。
矩阵B和矩阵C中每个数字都是不超过1000的非负整数。
Output
输出最大的D。
1 2 3 4 5
| 3 1 2 1 3 1 0 1 2 3 2 3 7
|
Sample Output
Hint
1≤N≤500
标签:网络流
最大权闭合子图
Solution
先化简一下D,用和式表示:
D=(A1A2⋯An)×⎝⎜⎜⎜⎜⎛B1,1B2,1⋮Bn,1B1,2B2,2⋮Bn,2⋯⋯⋱⋯B1,nB2,n⋮Bn,n⎠⎟⎟⎟⎟⎞×⎝⎜⎜⎜⎜⎛A1A2⋮An⎠⎟⎟⎟⎟⎞−(C1C2⋯Cn)×⎝⎜⎜⎜⎜⎛A1A2⋮An⎠⎟⎟⎟⎟⎞=(∑i=1nAiBi,1∑i=1nAiBi,2⋯∑i=1nAiBi,n)×⎝⎜⎜⎜⎜⎛A1A2⋮An⎠⎟⎟⎟⎟⎞−(C1C2⋯Cn)×⎝⎜⎜⎜⎜⎛A1A2⋮An⎠⎟⎟⎟⎟⎞=i=1∑nj=1∑nAiAjBi,j−i=1∑nAiCi
由于A是01矩阵,可以将其看作有n个物品,选择取或不去,第i给物品取有Ci的代价,第i个和第j个同时取会有Bi,j的贡献。要求使总贡献D最大。
对于每个物体建一个点,任意两个物体的组合建一个点。对于组合<i,j>,连接其代表的点和第i个物体的点与第j个物体的点。对于物体的点,权值设为−Ci,对于组合的点,权值设为Bi,j。这样选择一个组合的点,必须将其连向的两个物体的点也选择,即变为最大权闭合子图模型。
建模:
- 对每个物体i建一个点pi,连接pi→T流量Ci
- 对每个组合<i,j>建一个点pi,j,连接S→pi,j流量Bi,j
- 对于每个组合<i,j>,连边pi,j→pi流量∞,连边pi,j→pj流量∞
跑最大流求最小割,答案为∑i=1n∑j=1nBi,j−最小割。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <bits/stdc++.h> #define MAX_N 300000 #define MAX_M 2000000 #define INF 0x3f3f3f3f using namespace std; template <class T> inline void read(T &x) { x = 0; int c = getchar(), f = 1; for (; !isdigit(c); c = getchar()) if (c == 45) f = -1; for (; isdigit(c); c = getchar()) (x *= 10) += f*(c-'0'); } int n, s, t, cnt, tot; int d[MAX_N+5], pr[MAX_N+5], cr[MAX_N+5]; struct node {int v, c, nxt;} E[MAX_M+5]; void init() {s = 0, t = n*n+n+1, cnt = 0, memset(pr, -1, sizeof pr);} void insert(int u, int v, int c) {E[cnt] = (node){v, c, pr[u]}, pr[u] = cnt++;} void addedge(int u, int v, int c) {insert(u, v, c), insert(v, u, 0);} bool BFS() { queue <int> que; que.push(s); memset(d, -1, sizeof d), d[s] = 0; while (!que.empty()) { int u = que.front(); que.pop(); for (int i = pr[u]; ~i; i = E[i].nxt) { int v = E[i].v, c = E[i].c; if (~d[v] || !c) continue; d[v] = d[u]+1, que.push(v); } } return ~d[t]; } int DFS(int u, int flow) { if (u == t) return flow; int ret = 0; for (int &i = pr[u]; ~i; i = E[i].nxt) { int v = E[i].v, c = E[i].c; if (d[u]+1 != d[v] || !c) continue; int tmp = DFS(v, min(flow, c)); E[i].c -= tmp, E[i^1].c += tmp; flow -= tmp, ret += tmp; if (!flow) break; } if (!ret) d[u] = -1; return ret; } void cpy() {for (int i = s; i <= t; i++) cr[i] = pr[i];} void rec() {for (int i = s; i <= t; i++) pr[i] = cr[i];} int Dinic() {int ret = 0; cpy(); while (BFS()) ret += DFS(s, INF), rec(); return ret;} int main() { read(n), init(); for (int i = 1; i <= n; i++) for (int j = 1, b; j <= n; j++) read(b), tot += b, addedge(s, (i-1)*n+j, b); for (int i = 1, c; i <= n; i++) read(c), addedge(n*n+i, t, c); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) addedge((i-1)*n+j, n*n+i, INF), addedge((i-1)*n+j, n*n+j, INF); return printf("%d\n", tot-Dinic()), 0; }
|