寒假集训 最小矩形覆盖

Description

你有\(N\)个矩形,每个矩形都有一个宽\(W_i\)和高\(H_i\)。你的任务是在平面内找一个新矩形,使得\(N\)个矩形都严格在这个新矩形里面,且新矩形内的\(N\)个矩形不能重叠(可以有公共边)。

Hint

对于\(100%\)的数据,\(n <= 6, 1 <= W_i,H_i <= 1000\)。

Solution

这道题的解法比较奇葩。

对于平面内的任意一种合法放置矩形的方案,我们可以这样搞: 一种放置方案

对于没个矩形线条从右上角开始能往左就往左,然后向下。

我们发现这些线条逆时针看起来有一定的规律,别急,我们把这个顺序存入\(S_1\)。

然后我们把矩形的左上-右下按类似方法,构建相对应的线条,把顺序存入\(S_2\)。

然后得到这两个序列,如果对于一对\(i, j\),如果他们在\(S_1\)中的出现位置\(P\)有\(P_i < P_j\)且在\(S_2\)中的出现位置\(P\)有\(P_i < P_j\),则我们说\(i\)在\(j\)方;如果他们在\(S_1\)中的出现位置\(P\)有\(P_i < P_j\)且在\(S_2\)中的出现位置\(P\)有\(P_i < P_j\),则我们说\(i\)在\(j\)方。

有定理可以得出如果给定\(S_1, S_2\),我们可以确定有且仅有一个摆放方案与之对应,反之亦然。

我们把具有“上”关系的两点和“左”关系的两点之间连一条有向边,构成两个拓扑图。

然后我们可以暴力求\(S_1, S_2\)的排列,然后对于每种排列顺序,我们构建两个拓扑图,在图上跑一个DP一样的东西求最长路。

那么答案就是这两条最长路的长度之积了。

对于答案取\(min\)即可。

复杂度分析:

枚举排列:\(\Theta(N!*N!)\)

建图+最长路:\(\Theta(N^3)\)

总复杂度:\(\Theta(N!*N!*N^3)\)

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
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
#include <cstdio>
int n, w[10], h[10];
int s1[10], s2[10];
bool v1[10], v2[10];
int getint() {
	int r = 0; char c = getchar();
	for (; c < '0' || c > '9'; c = getchar());
	for (; '0' <= c && c <= '9'; c = getchar()) r = r * 10 - '0' + c;
	return r;
}
int ans = (1<<30);
int au[10], al[10];
int mu, ml;
int up[10][10], left[10][10], du[10], dl[10];
int max(int a, int b){return a > b ? a : b;}
int min(int a, int b){return a < b ? a : b;}
bool uu[10], ul[10];
void UU() {
	int i = 0;
	while (i < n)
		for (int j = 1; j <= n; ++j)
			if (du[j] == 0 && !uu[j]) {
				++i;
				uu[j] = true;
				for (int k = 1; k <= n; ++k)
					if (up[j][k]) {
						--du[k];
						up[j][k] = 0;
						au[k] = max(au[k], au[j]+h[k]);
					}
			}
}
void UL() {
	int i = 0;
	while (i < n)
		for (int j = 1; j <= n; ++j)
			if (dl[j] == 0 && !ul[j]) {
				++i;
				ul[j] = true;
				for (int k = 1; k <= n; ++k)
					if (left[j][k]) {
						--dl[k];
						left[j][k] = 0;
						al[k] = max(al[k], al[j]+w[k]);
					}
			}
}
void solve() {
	int i = 0;
	for (i = 1; i <= n; ++i) {
		uu[i] = ul[i] = false;
		for (int j = i+1; j <= n; ++j) {
			up[s1[i]][s1[j]] = 1;
			du[s1[j]]++;
		}
	}
	for (i = 1; i <= n; ++i)
		for (int j = i+1; j <= n; ++j)
			if (up[s2[j]][s2[i]]) {
				up[s2[j]][s2[i]] = 0;
				du[s2[i]]--;
				left[s2[j]][s2[i]] = 1;
				dl[s2[i]]++;
			}
	for (i = 1; i <= n; ++i) au[i] = h[i], al[i] = w[i];
	mu = 0; ml = 0;	UU(); UL();	for (i=1;i<=n;++i)mu=max(mu,au[i]), ml=max(ml,al[i]);
	ans = min(ans, mu*ml);
	return;
}
void p2(int now) {
	if (now > n) {
		solve();
		return;
	}
	for (int i = 1; i <= n; ++i)
		if (!v2[i]) {
			s2[now] = i;
			v2[i] = true;
			p2(now+1);
			v2[i] = false;
		}
}
void p1(int now) {
	if (now > n) {
		p2(1);
		return;
	}
	for (int i = 1; i <= n; ++i)
		if (!v1[i]) {
			s1[now] = i;
			v1[i] = true;
			p1(now+1);
			v1[i] = false;
		}
}
int main () {
	freopen("rectangle.in", "r", stdin);
	freopen("rectangle.out", "w", stdout);
	n = getint();
	for (int i = 1; i <= n; ++i) {
		w[i] = getint();
		h[i] = getint();
	}
	p1(1);
	printf("%d\n", ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}