首页 > ACM题库 > UVA > UVa OJ 103 – Stacking Boxes (嵌套盒子)
2013
12-30

UVa OJ 103 – Stacking Boxes (嵌套盒子)

Time limit: 3.000 seconds
限时:3.000秒

 

Background
背景

Some concepts in Mathematics and Computer Science are simple in one or two dimensions but become more complex when extended to arbitrary dimensions. Consider solving differential equations in several dimensions and analyzing the topology of an n-dimensional hypercube. The former is much more complicated than its one dimensional relative while the latter bears a remarkable resemblance to its “lower-class” cousin.
一些概念的一维或二维情况在数学和计算机学中是比较简单的,然而当维数不确定的情况时就复杂多了。比方说求解高阶的微分方程和分析n维超立方体的拓扑结构,前者要比其在一阶的情况复杂的多,而后者则与其较低阶的同类具有非常大的相似之处。

 

The Problem
问题

Consider an n-dimensional “box” given by its dimensions. In two dimensions the box (2,3) might represent a box with length 2 units and width 3 units. In three dimensions the box (4,8,9) can represent a box 4 × 8 × 9 (length, width, and height). In 6 dimensions it is, perhaps, unclear what the box (4,5,6,7,8,9) represents; but we can analyze properties of the box such as the sum of its dimensions.
给定一个n维的“盒子”的维数。在二维情况下,box (2, 3)可以表示一个长2个单位,宽3个单位的盒子。在三维情况下,box (4, 8, 9)可以表示一个4 × 8 × 9(长宽高)的盒子。在6维空间中可能无法搞清box (4,5,6,7,8,9)是什么样子,但我们可以分析盒子的性质,比如求其维度和。

In this problem you will analyze a property of a group of n-dimensional boxes. You are to determine the longest nesting string of boxes, that is a sequence of boxes b1, b2, …, bk such that each box bi nests in box bi+1 (1 ≤ i < k).
在这个问题中,你要分析一组n维盒子的某个性质。你要确定出盒子的最长嵌套串,也就是说一系列的盒子b1、b2、……、bk,一个套一个,使所有的bi都嵌套在bi+1内。

A box D = (d1, d2, …, dn) nests in a box E = (e1, e2, …, en) if there is some rearrangement of the di such that when rearranged each dimension is less than the corresponding dimension in box E. This loosely corresponds to turning box D to see if it will fit in box E. However, since any rearrangement suffices, box D can be contorted, not just turned (see examples below).
对于一个盒子D = (d1, d2, …, dn)和另一个盒子E = (e1, e2, …, en),如果存在一种di的排列,使重排的每个维度的值都小于盒子E中对应维度的值,则盒子D可嵌入盒子E。这个过程和旋转盒子D,看它是否能套入E的过程类似。然而,若需满足所有维度的排列,盒子D必须是可以扭曲的,而不仅仅是旋转。

For example, the box D = (2,6) nests in the box E = (7,3) since D can be rearranged as (6,2) so that each dimension is less than the corresponding dimension in E. The box D = (9,5,7,3) does NOT nest in the box E = (2,10,6,8) since no rearrangement of D results in a box that satisfies the nesting property, but F = (9,5,7,1) does nest in box E since F can be rearranged as (1,9,5,7) which nests in E.
比如,盒子D = (2,6)可嵌入盒子E= (7,3)是由于盒子可以排列为(6,2),以使每个维度都小于E中的相应维度。而盒子D = (9,5,7,3)就不能嵌入盒子E = (2,10,6,8),因为不存在D的一种排列可以满足嵌入的性质,但是F = (9,5,7,1)就可以通过排列为(1,9,5,7)而嵌入E。

Formally, we define nesting as follows: box D = (d1, d2, …, dn) nests in box E = (e1, e2, …, en) if there is a permutation p of 1…n such that (dp(1), dp(2), …, dp(n)) “fits” in (e1, e2, …, en) i.e., if dp(i) < ei for all 1 ≤ i ≤ n.
我们正式的定义嵌套关系如下:盒子D = (d1, d2, …, dn)和E = (e1, e2, …, en),当存在一种1…n的排列p使得(dp(1), dp(2), …, dp(n))能够匹配(e1, e2, …, en),即对所有的1 ≤ i ≤ n都有dp(i) < ei,那么就认为D可以嵌入E。

 

The Input
输入

The input consists of a series of box sequences. Each box sequence begins with a line consisting of the the number of boxes k in the sequence followed by the dimensionality of the boxes, n (on the same line.)
输入由一组的盒子序列构成。每个盒子序列的开始是盒子的总数k和维度n(出现在同一行)。

This line is followed by k lines, one line per box with the n measurements of each box on one line separated by one or more spaces. The ith line in the sequence (1 ≤ i ≤ k) gives the measurements for the ith box.
第一行下面有k行,每行用n个(n由第一行确定)数表示一个盒子,这n个数由一个或多个空格隔开。序列中的第i行(1 ≤ i ≤ k)表示第i个盒子。

There may be several box sequences in the input file. Your program should process all of them and determine, for each sequence, which of the k boxes determine the longest nesting string and the length of that nesting string (the number of boxes in the string).
输入的数据中可能存在多个盒子序列。你的程序应该处理全部数据,针对每个序列,找出k个盒子中的哪几个可以构成最长的嵌套串,以及这个嵌套串的长度(嵌套串中盒子的数量)。

In this problem the maximum dimensionality is 10 and the minimum dimensionality is 1. The maximum number of boxes in a sequence is 30.
在这个问题中最大的维度是10,最小的维度是1。最大的盒子序列长度为30。

 

The Output
输出

For each box sequence in the input file, output the length of the longest nesting string on one line followed on the next line by a list of the boxes that comprise this string in order. The “smallest” or “innermost” box of the nesting string should be listed first, the next box (if there is one) should be listed second, etc.
对应输入的每个盒子序列,应在第一行输出最长的嵌套串长度,在下一行按顺序输入组成这个嵌套串的盒子列表。嵌套串中最“小”的或着说是最“内”的盒子应该放在第一位,之外的一个(如果存在)放在第二位,以此类推。

The boxes should be numbered according to the order in which they appeared in the input file (first box is box 1, etc.).
盒子的编号要按它们在输入数据中的位置相对应。(输入的第一个盒子为1号,第二个盒子为2号,等等)

If there is more than one longest nesting string then any one of them can be output.
如果存在多于一个嵌套串的长度同为最长,可输出其中的任何一个。

 

Sample Input
输入示例

5 2
3 7
8 10
5 2
9 11
21 18
8 6
5 2 20 1 30 10
23 15 7 9 11 3
40 50 34 24 14 4
9 10 11 12 13 14
31 4 18 8 27 17
44 32 13 19 41 19
1 2 3 4 5 6
80 37 47 18 21 9

 

Sample Output
输出示例

5
3 1 2 4 5
4
7 2 5 6

 

Analysis
分析

每个盒子先将各维度的值从大到小排序。如果A盒可放入B盒,那么A盒各维度的值都应小于B盒对应维度的值。在明确大小关系后,可采用最长递增序列(Longest Increasing Subsequence,LIS)算法来解决。该算法要求所给原序列有序,因此需要对盒子排序,先按第一维度值排序,如果相等再比较第二维度,以此类推。关于LIS算法,我将在另外的文章中给出。基本算法明确后,本题就没有其它的难度了,按思路编码即可。

 

Solution
解答

 

#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
using namespace std;
//盒子类
class BOX {
public:
	//按文字顺序比较,用于对多个盒子进行排序
	bool operator<=(const BOX& other) const {
		return (lexicographical_compare(vecUnit.begin(), vecUnit.end(),
			other.vecUnit.begin(), other.vecUnit.end()));
	}
	//判断是否可以嵌套,用于LIS算法
	bool Contain(const BOX& other) const {
		return (mismatch(vecUnit.begin(), vecUnit.end(),
			other.vecUnit.begin(), greater<int>()).first == vecUnit.end());
	}
	//记录盒子的编号和维度
	int nId; vector<int> vecUnit;
};
//主函数
int main(void) {
	//循环处理每行输入数据
	for (int nDemCnt, nBoxCnt; cin >> nBoxCnt >> nDemCnt;) {
		vector<BOX> vecBoxes(nBoxCnt), aMat[30][30];
		//循环输入每个盒子
		for (int i = 0; i < nBoxCnt; i++) {
			vecBoxes[i].nId = i;
			vector<int> &vecUnit = vecBoxes[i].vecUnit;
			//循环输入每个维度到盒子中
			for (int j = 0, nUnit; j < nDemCnt; ++j) {
				cin >> nUnit;
				vecUnit.push_back(nUnit);
			}
			//并将盒子的维度值从大到小排序
			sort(vecUnit.begin(), vecUnit.end(), greater<int>());
		}
		//按文字顺序从小到大排列所有盒子
		sort(vecBoxes.begin(), vecBoxes.end(), less_equal<BOX>());
		//vecMax记录以每个盒子为起始的最大嵌套长度
		//vecPrev记录上一个盒子的位置
		vector<int> vecMax(nBoxCnt, 0), vecPrev(nBoxCnt, -1);
		//开始LIS算法
		int nMax = 0;
		vecMax.front() = 1;
		for (int i = 1; i < nBoxCnt; ++i) {
			for (int j = 0; j < i; ++j) {
				//找出之前能嵌入当前盒子且拥有最大嵌套序列长度的盒子
				if ((vecMax[j] > vecMax[i] || vecMax[i] == 0) &&
					vecBoxes[i].Contain(vecBoxes[j])) {
					//将最大嵌套序列长度更新到当前盒子
					vecMax[i] = vecMax[j];
					vecPrev[i] = j;
				}
			}
			//当前盒子的最大嵌套序列长度要加上自身的1
			if (++vecMax[i] > vecMax[nMax]) {
				//找出所有盒子中的拥有最大嵌套序列的盒子
				nMax = i;
			}
		}
		//跟据vecPrev数组回溯,找出所有盒子
		for (vecMax.clear(); nMax != -1; nMax = vecPrev[nMax]) {
			vecMax.push_back(nMax);
		}
		//反转输出结果
		vector<int>::reverse_iterator ri = vecMax.rbegin();
		cout << vecMax.size() << '\n' << vecBoxes[*ri++].nId + 1;
		for (; ri != vecMax.rend(); ++ri) {
			cout << ' ' << vecBoxes[*ri].nId + 1;
		}
		cout << endl;
	}
	return 0;
}

 

 

转自:http://www.cnblogs.com/devymex/archive/2010/08/04/1792138.html


  1. 算法是程序的灵魂,算法分简单和复杂,如果不搞大数据类,程序员了解一下简单点的算法也是可以的,但是会算法的一定要会编程才行,程序员不一定要会算法,利于自己项目需要的可以简单了解。