[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\)

    1. 如果在残量网络中,行 \(i\) 到 列 \(j\) 的流量 为 1,就能不放 1, 并断掉这条边。

    2. 如果没有流量,那就说明一定有 \(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;
} 
posted @ 2022-02-18 15:43  qjbqjb  阅读(60)  评论(0编辑  收藏  举报