[USACO1.4] 铺放矩形块 Packing Rectangles

题目描述

给定 44 个矩形块,找出一个最小的封闭矩形将这 44 个矩形块放入,但不得相互重叠。所谓最小矩形指该矩形面积最小。

44 个矩形块中任一个矩形的边都与封闭矩形的边相平行,上图显示出了铺放 44 个矩形块的 66 种方案。

这 66 种方案是唯一可能的基本铺放方案。因为其它方案能由基本方案通过旋转和镜像反射得到。

可能存在满足条件且有着同样面积的各种不同的封闭矩形,你应该输出所有这些封闭矩形的边长。

输入格式

共有 44 行,每行两个正整数,表示每个矩形的边长。

输出格式

总行数为解的总数加一。

第一行是一个整数,代表封闭矩形的最小面积。
接下来的每一行都表示一个解,由 p,q\space (p \leqslant q)p,q (p⩽q) 来表示。这些行必须根据 pp 的大小按升序排列,且所有行都应是不同的。

输入输出样例

输入 #1复制

1 2
2 3
3 4
4 5

输出 #1复制

40
4 10
5 8

说明/提示

【数据范围】
对于 100\%100% 的数据,输入的所有数在 [1,50][1,50] 内。

题目翻译来自NOCOW。

USACO Training Section 1.4

这道题就是暴力搜索题,写一个 DFS 就可以完美解决了,主要是对于各种情况的处理,本蒟蒻将一一讲解。

其实本蒟蒻想不出来这些摆放的情况,但是题目画了图啊 awa。

几种情况的图片: 

接下来我们用 a,ba,b 来表示封闭矩阵的宽和长,x1, x2, x3, x4x1,x2,x3,x4 分别表示四个矩阵的宽,y1, y2, y3, y4y1,y2,y3,y4 分别表示四个矩阵的长。

不管这个矩阵怎么歪,我们只把对 aa 的值有贡献的边记作 x_{i}xi​,对 bb 有贡献的记作 y_{i}yi​。(1 \le i \le 4)(1≤i≤4)。

第一种情况

四种并排放。 很容易可以看出:a = x1 + x2 + x3 + x4a=x1+x2+x3+x4。

bb 的值就为他们中的最大值,所以 b = \max(\max(y1, y2), \max(y3, y4))b=max(max(y1,y2),max(y3,y4))。

第二种情况

三个并排,一个躺平。 也可以得出:a = \max(x1 + x2 + x3, x4)a=max(x1+x2+x3,x4)。

以及:b = \max(y1,\max(y2,y3)) + y4b=max(y1,max(y2,y3))+y4。

第三种情况

显然:a = \max(x1 + x2, x3) + x4a=max(x1+x2,x3)+x4。

b = \max(\max(y1, y2) + y3, y4)b=max(max(y1,y2)+y3,y4)。

第四种情况

看似更复杂,其实就是堆在一起的 2,3 号矩阵难以处理。但细想想也不难,堆在上面的那个的 xx 值一定小于等于下面那个,所以这两个矩阵对于 aa 的值的贡献为:\max(x2, x3)max(x2,x3)。

由此可得:a = x1 + \max(x2, x3) + x4a=x1+max(x2,x3)+x4。

b = \max(y1, \max(y2 + y3, y4))b=max(y1,max(y2+y3,y4))。

第五种情况

与第四种基本相同,区别在于第四种堆叠的矩阵位于中间,第五种位于旁边

显然:a = \max(x1, x2) + x3 + x4a=max(x1,x2)+x3+x4。

b = \max(y1 + y2, \max(y3, y4))b=max(y1+y2,max(y3,y4))。

第六种情况

最复杂的情况,看似复杂实则也很容易懂,要分情况讨论,想必各位神犇做数学题时没少遇到。

我们统一一下编号:右上角,左上角,右下角,左下角,按照这个顺序从 1 到 4 编号。

情况一:y4 + y2 \le y3y4+y2≤y3

各位不妨画个图,立刻就可以得出:a = \max(\max(x1, x3 + x2), x3 + x4)a=max(max(x1,x3+x2),x3+x4)。

情况二:在不满足情况一的条件下,y4 < y3y4<y3

依然可以轻松得出:a = \max(x1 + x2, \max(x2, x4) + x3)a=max(x1+x2,max(x2,x4)+x3)。

情况三:y3 = y4y3=y4

这是所有情况中最直观的:a = \max(x1 + x2, x3 + x4)a=max(x1+x2,x3+x4)。

情况四:y3 < y4y3<y4,且不满足情况五

依然是画了图以后就可以轻松得出:a = \max(x1 + x2, \max(x1, x3) + x4)a=max(x1+x2,max(x1,x3)+x4)。

情况五:y1 + y3 \le y4y1+y3≤y4

动手画图后可以得出:a = \max(x2, \max(x1, x3) + x4)a=max(x2,max(x1,x3)+x4)。

看到这可能有神犇会好奇,bb 咋求?

画图啊,画图一下子就出来了:b = \max(y1 + y3, y2 + y4)b=max(y1+y3,y2+y4)。

代码

具体应该怎么做呢? 先深搜枚举位置(也就是上文中编号),然后调用计算就行了。

非常的简单,就是很肝。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[5][2], num[5]; //分别储存每个矩阵的长宽,顺序
int cnt, Min = INT_MAX;
bool vis[5], flag[402][402];
struct node {
	int p, q;
} ans[2050];//结构体排序完成子任务
void answer (int sx, int sy) {
	if (sx > sy) swap(sx, sy);//也是子任务	
	if (flag[sx][sy]) return;//防止重复
	flag[sx][sy] = true;
	if (sx * sy < Min) {
		Min = sx * sy;
		cnt = 1;
		ans[cnt].p = sx, ans[cnt].q = sy;
	} else if (sx * sy == Min) {
		ans[++cnt].p = sx;
		ans[cnt].q = sy;
	}
}
void f (int s1, int s2, int s3, int s4) {
	int x, y;
	x = a[s1][1] + a[s2][1] + a[s3][1] + a[s4][1], y = max(max(a[s1][0], a[s2][0]), max(a[s3][0], a[s4][0]));
	answer(x, y);
	x = max(a[s1][1] + a[s2][1] + a[s3][1], a[s4][1]), y = max(a[s1][0], max(a[s2][0], a[s3][0])) + a[s4][0];
	answer(x, y);
	x = max(a[s1][1] + a[s2][1], a[s3][1]) + a[s4][1], y = max(a[s4][0], max(a[s1][0], a[s2][0]) + a[s3][0]);
	answer(x, y);
	x = a[s1][1] + max(a[s2][1], a[s3][1]) + a[s4][1], y = max(a[s4][0], max(a[s1][0], a[s2][0] + a[s3][0]));
	answer(x, y);
	x = max(a[s1][1], a[s2][1]) + a[s3][1] + a[s4][1], y = max(max(a[s1][0] + a[s2][0], a[s3][0]), a[s4][0]);
	answer(x, y);
	if (a[s3][0] >= a[s2][0] + a[s4][0]) x = max(max(a[s1][1], a[s3][1] + a[s2][1]), a[s3][1] + a[s4][1]);
	else if (a[s4][0] < a[s3][0]) x = max(a[s1][1] + a[s2][1], max(a[s2][1], a[s4][1]) + a[s3][1]);
	else if (a[s3][0] == a[s4][0]) x = max(a[s1][1] + a[s2][1], a[s3][1] + a[s4][1]);
	else if (a[s3][0] < a[s4][0] && a[s4][0] < a[s3][0] + a[s1][0]) x = max(a[s1][1] + a[s2][1], max(a[s1][1], a[s3][1]) + a[s4][1]);
	else if (a[s4][0] >= a[s1][0] + a[s3][0]) x = max(a[s2][1], max(a[s1][1], a[s3][1]) + a[s4][1]);
	y = max(a[s1][0] + a[s3][0], a[s2][0] + a[s4][0]);
	answer(x, y);
}//上文有详细讲,这里不再赘述
void dfs (int k) {
	if (k == 5) {
		f(num[1], num[2], num[3], num[4]);
		return;
	}
	for (int i = 1; i <= 4; i++) {
		if (vis[i]) continue;
		vis[i] = true;
		num[k] = i;
		dfs(k + 1);
		swap(a[i][0], a[i][1]);//把矩阵转90°再搜
		dfs(k + 1);
		swap(a[i][0], a[i][1]);
		vis[i] = false;//回溯
	}
}
bool cmp (node a, node b) {return a.p < b.p;}
int main () {
	for (int i = 1; i <= 4; i++) scanf("%d%d", &a[i][0], &a[i][1]);
	dfs(1);
	sort(ans + 1, ans + 1 + cnt, cmp);
	printf("%d\n", Min);
	for (int i = 1; i <= cnt; i++) printf("%d %d\n", ans[i].p, ans[i].q);//完美的输出
	return 0;
}

这道题算是绿题中比较简单的了,而且还是一次水两道 awa。

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值