[TJOI2011]构造矩阵
题意
LG P1418
给出 \(n * m(n,m\leq 100)\) 的 01 矩阵 的 行和列的 1 的个数限制, 求满足这些限制中字典序最小的矩阵。
定义字典序最小为从第一行开始比较,行的字典序小就是字典序小,相等则比较下一行。
贪心
不难想到贪心,尽可能把第一行限制的 1 放到靠后的位置,这样的字典序一定最小。
但是要求方案合法,如果直接搜索,状态很多,判定也很耗时。
有没有快速判定满足限制的合法状态存在呢?
网络流
用网络流判定矩阵合法状态。
- 建模:
总共 \(n + m + 2\) 个点,行有 \(n\) 个点,列有 \(m\) 个点。
行的个数限制就是从源点 \(S\) 到 行 \(i\),流量是限制个数的边。
列的限制同理。
原矩阵的点 \((i, j)\) 就是行 \(i\) 和 行 \(j\) 的一条流量为 \(1\) 的边。
跑一边网络流,如果 \(\text{maxflow} = \sum x\) 就能得到一个合法的状态。
-
判定 \((i, j)\) 能否不放 \(1\)。
-
如果在残量网络中,行 \(i\) 到 列 \(j\) 的流量 为 1,就能不放 1, 并断掉这条边。
-
如果没有流量,那就说明一定有 \(S \to i \to j \to T\) 的增广路,先退流,后断边,再跑一遍网络流,如果有 \(\text{maxflow} = 1\), 那么说明有合法状态存在,没有就要恢复。
-
网络流跑二分图时间复杂度 \(O(n\sqrt{m})\)。
感觉是 \(O(n^4)\) 的。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 510;
const int INF = 0x7fffffff;
const int mod = 1000000007;
template <typename T>
void Read(T &x) {
x = 0; T f = 1; char a = getchar();
for(; a < '0' || '9' < a; a = getchar()) if (a == '-') f = -f;
for(; '0' <= a && a <= '9'; a = getchar()) x = (x * 10) + (a ^ 48);
x *= f;
}
int n, m;
int x[MAXN], y[MAXN];
struct edge {
int point, len, next;
} e[MAXN * MAXN * 2];
int first[MAXN * 2 + 2];
int cnt = 1;
void add(int u, int v, int w) {
++ cnt;
e[cnt].point = v;
e[cnt].next = first[u];
e[cnt].len = w;
first[u] = cnt;
}
int S, T;
int dep[MAXN], cur[MAXN * 2 + 2];
bool bfs() {
queue<int> q;
q.push(S);
memset(dep, -1, sizeof(dep));
memcpy(cur, first, sizeof(first));
dep[S] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = first[u]; i; i = e[i].next) {
int v = e[i].point;
if (dep[v] == -1 && e[i].len) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
}
return dep[T] != -1;
}
int dfs(int u, int flow) {
if (u == T) return flow;
int sum = 0;
for (int &i = cur[u]; i; i = e[i].next) {
int v = e[i].point;
if (dep[v] != dep[u] + 1 || !e[i].len) continue;
int out = dfs(v, min(flow, e[i].len));
e[i].len -= out, sum += out;
e[i ^ 1].len += out, flow -= out;
}
return sum;
}
int dinic() {
int sum = 0;
while (bfs()) {
sum += dfs(S, INF);
}
return sum;
}
int id[MAXN * 2 + 2][MAXN * 2 + 2];
bool col(int X, int Y) {
dinic();
if (!x[X] || !y[Y]) return 1;
Y += n;
if (e[id[X][Y]].len) {
x[X] --, y[Y - n] --;
e[id[X][Y]].len = 0;
return 0;
} else {
e[id[S][X]].len ++, e[id[S][X] ^ 1].len --;
e[id[X][Y]].len = e[id[X][Y] ^ 1].len = 0;
e[id[Y][T]].len ++, e[id[Y][T] ^ 1].len --;
if (dinic() == 1) {
x[X] --, y[Y - n] --;
return 0;
} else {
e[id[S][X]].len --, e[id[S][X] ^ 1].len ++;
e[id[X][Y]].len = 0, e[id[X][Y] ^ 1].len = 1;
e[id[Y][T]].len --, e[id[Y][T] ^ 1].len ++;
return 1;
}
}
}
int node;
void Add(int x, int y, int z) {
id[x][y] = cnt + 1;
add(x, y, z);
add(y, x, 0);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> x[i];
for (int i = 1; i <= m; i ++) cin >> y[i];
S = n + m + 1, T = n + m + 2;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
if (j == 1) Add(S, i, x[i]);
if (i == 1) Add(j + n, T, y[j]);
Add(i, j + n, 1);
}
}
for (int i = 1; i <= n; i ++) x[i] = m - x[i];
for (int i = 1; i <= m; i ++) y[i] = n - y[i];
for (int i = 1; i <= n; i ++, cout << endl)
for (int j = 1; j <= m; j ++)
cout << col(i, j);
return 0;
}