【JZOJ1900】【2010集训队出题】矩阵 (经典2-SAT问题)
题意
有一个n∗n的矩阵B,一个1*n的矩阵C,你现在要构造一个1*n的0/1矩阵,令(A*B-C)*A^T=D,D只有一个元素,你要使得这个元素值最大。
n\leq 600
分析
推一下矩阵乘法的式子就能转换为这样的问题:
有n个元素编号为1 \sim n,选择i将获得-c_i的贡献,同时选择i,j将获得b_{i,j}的贡献,找出一种方案使得贡献最大。
建立源点S,向中间一排n个点连边,边权为\sum b_{i,k},这些边存在的意义是a_i=1。中间一排点向汇点T连边,边权为c_i,这些边存在的意义是a_i=0。中间的点两两连边,边权为b_{i,j}。
思考这个网络的最小割的意义。对于中间某个点i,显然(S,i)和(i,T)两条边有且仅有一条被割,割(S,i)意义为a_i=0,割(i,T)意义为a_i=1。当a_i,a_j有一个为0时,必定存在S,i,j,T的路径,b_{i,j}这条边就会割掉,贡献就会减去。综上,这个网络的最小割就对应原模型的解。
Code
#include <cstdio>
#include <cstring>
const int N = 617, INF = 2147483647;
inline int read()
{
int x = 0, f = 0;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
return f ? -x : x;
}
int min(int a, int b) { return a < b ? a : b; }
int n;
int ans, b[N][N], c[N];
int tot = 1, st[N], to[N * N * 4], nx[N * N * 4], len[N * N * 4], gap[N];
int cur[N];
void add(int u, int v, int w)
{
to[++tot] = v, nx[tot] = st[u], len[tot] = w, st[u] = tot;
to[++tot] = u, nx[tot] = st[v], len[tot] = 0, st[v] = tot;
}
int S, T, h, t, q[N], dep[N];
void bfs()
{
memset(dep, -1, sizeof(dep));
memset(gap, 0, sizeof(gap));
h = 1, q[t = 1] = T, dep[T] = 0;
while (h <= t)
{
int u = q[h++];
++gap[dep[u]];
for (int i = st[u]; i; i = nx[i]) if (dep[to[i]] == -1) dep[to[i]] = dep[u] + 1, q[++t] = to[i];
}
}
int dinic(int u, int flow)
{
if (u == T) return flow;
int rest = flow, tmp;
for (int i = cur[u]; i; i = nx[i])
{
cur[u] = i;
if (len[i] > 0 && dep[u] == dep[to[i]] + 1)
{
tmp = dinic(to[i], min(rest, len[i]));
len[i] -= tmp, len[i ^ 1] += tmp, rest -= tmp;
if (!rest) return flow;
}
}
--gap[dep[u]];
if (gap[dep[u]] == 0) dep[S] = n + 3;
++dep[u];
++gap[dep[u]];
return flow - rest;
}
int main()
{
//freopen("matrix.in", "r", stdin);
n = read();
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) b[i][j] = read(), ans += b[i][j];
for (int i = 1; i <= n; i++) c[i] = read();
S = 0, T = n + 1;
for (int i = 1; i <= n; i++)
{
int s = 0;
for (int j = 1; j <= n; j++) s += b[i][j];
add(S, i, s), add(i, T, c[i]);
}
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i != j) add(i, j, b[i][j]);
bfs();
while (dep[S] < n + 2) memcpy(cur, st, sizeof(st)), ans -= dinic(S, INF);
printf("%d\n", ans);
return 0;
}
作者:zjlcnblogs
出处:https://www.cnblogs.com/zjlcnblogs/p/11349083.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】2025 HarmonyOS 鸿蒙创新赛正式启动,百万大奖等你挑战
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】开源 Linux 服务器运维管理面板 1Panel V2 版本正式发布
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个 java 空指针异常的解决过程
· 揭开 SQL Server 和 PostgreSQL 填充因子的神秘面纱
· 没有调度器的协程不是好协程,零基础深入浅出 C++20 协程
· 别做抢活的导演:代码中的抽象层次原则
· 从 Redis 客户端超时到 .NET 线程池挑战
· 会Vibe Coding的同事:我一个人干掉整个技术部!
· 回答准确率从60%飙至95%!AI知识库救命方案
· dotnetty 内存泄漏的BUG修复了
· 20250709 - GMX V1 攻击事件: 重入漏洞导致的总体仓位价值操纵
· 线上问题定位神器:Arthas