积木搭建解题报告
2015-07-31 14:14
295 查看
[问题描述]
单位立方体(unit cube)是一个1×1×1的立方体,它的所有角的x,y和z坐标都是整数。我们称两个单位立方体是连通的,当且仅当他们有一个公共的面;一个构形(solid)或者是一个单位立方体,或者是由多个单位立方体组成的连通物体(如图一所示),它的体积就是它所含的单位立方体的个数。
一块积木(block)是一个体积不超过4的构形。如果两块积木可以平移、旋转(注意,不能成镜像)操作变得完全一样,我们就称它们属于同一类。这样,积木总共有12类(如图2所示)。图2中积木的不同颜色仅仅是为了帮助你看清楚它的结构,并没有其他的含义。
一个构形可以由若干块积木组成。如果用一些积木可以互不重叠的搭建出某个构形,那么就称这些积木是这个构形的一种分解(decomposition)。
你的任务是写一个程序,对于给定的一组积木种类和一个构形S,求出境可能少的积木个数,使得他们能够搭建出构形S。你只需给出这个最少的积木个数,以及这些积木各自所属的种类,而不必给出具体的搭建方案。
[输入输出格式]
(1)输入:
在输入文件中,一个单位立方体用同一行中的三个整数x,y,z表示。坐标(x,y,z)是该单位立方体各个顶点坐标{(xi,yi,zi)|i=1..8}中xi+yi+zi最小的。
输入文件有两个。第一个输入文件描述了积木的不同种类,文件为types.in。在下问“输入输出样例”部分给出了一个types.in文件,他一次定义了图2所示的12中积木。注意:所有的测试数据都将统一采用该types.in文件。
每一种类的积木由连续的若干行描述。第一行市积木的种类编号I(1≤I≤12);第二行是该类积木的体积V(1≤V≤4);随后的V行每行包含三个整数x,y和z,表示组成该类积木的一个单位立方体,其中1≤x,y,z≤4。
(2)输出:
输出的文件名为:block.out。
它的第一行是一个正整数M,表示使用积木的最少块数。
文件的第二行列出M个整数,表示搭建出构形S的M块积木各自的种类。
[输入输出样例]
[数据结构]
为描述清楚,先列出数据结构:
[算法分析]
看到这个题目,就会想到搜索。这是一道有一定难度的题目,盲目的搜索是不行的,因为题目中说明了每种积木都可以经过翻转和平移,然而,只要得到了平移或翻转后的积木,我们就只需要一个一个的往构形里面填,直到填满,这显然是一个深度优先搜索。然而对于这道题目来说,除了搜索之外,还有一个难点,就是生成所有的翻转或平移后的积木状态。
我们考虑用一个三员组表示空间中的1*1*1的立方体(单位立方体),三员组(x,y,z)表示空间(x,y,z)处有一个单位立方体。这样我们就有三种数据结构:
右图所示图形可表示为:
(0,0,0) (0,0,1) (0,1,1)
怎样生成所有状态:
首先,题目中指出:可以平移,翻转,但不可以成镜像。如何处理平移?我们只要保存一个积木中四个顶点的相对位置,在搜索的时候自然就可以完成平移,在此我们规定一个积木的一个单位立方体为(0,0,0),则其余三个可以用相对于这个单位立方体的位置来表示。如何处理翻转?由于题目规定不可以成镜像,所以,我们在生成所有状态时的翻转过程中,必成偶数次镜像(因为:成偶数次镜像等于没成镜像)。由于旋转积木很难理解,也有可能会遗漏一些情况,我们采用的是旋转坐标轴的方法。
可以这么理解旋转坐标轴法:
顾名思义,就是要在三维空间中,将坐标轴旋转,当然,旋转后的坐标要两两垂直,在满足这个条件的情况下,用temp:TCube表示某积木cube Tcube经过某种旋转后所得的状态。设func(temp)表示有几条坐标线(射线)所在直线和原来这条坐标线所在直线的不重合的。考虑func(temp)的不同值:设f(temp)表示在func(temp)的基础上得到的表示temp性质的函数:
对于某一条特定的坐标,如果规定一个方向为正方向,则可以用g(temp)表示旋转后的新坐标线中方向和原来方向不同的条数。(注:这里的方向并非表示三维空间中的方向,只表示正负)。易得:若f(temp)+g(temp)为偶数,则temp是符合题意,即成偶数次镜像的。
搜索的时候,另需一个数组space:array[1..7,1..7,1..7] of byte;
space[x,y,z]=1表示此处未放, space[x,y,z]=0表示放了。
值得注意的是, 找一个可放置的积木时,我们不仅要枚举积木,而且要枚举点(tar[u,1],tar[u,2],tar[u,3])在积木中的位置。如下面的图形:我们要枚举点(tar[u,1],tar[u,2],tar[u,3])在积木中的四中位置情况,然后递归搜索,这样才能够全面。
至此,这道题目可以说是得到比较圆满解决了,按照这种方法便出来的程序速度是比较快的,能够对一半左右的测试数据。
[优化方法]
这里讲的优化,主要是针对搜索时的一些技巧来讲的。
在搜索的过程中,我们发现:我们的目的实际上就是要把一些积木放到构形里面去,不妨换个角度,把一个构形看成由许多单位立方体构成的,同样的,一个积木也是由许多单位立方体构成。我们把这个构形拆开承单个的单位立方体,每次从里面去出成分和某一块积木陈分相同一些单位立方体。因此,不妨令这些单位立方体是有序的,同样,积木的描述也是有序的。搜索时,由小到大去填充构形的某一个单位立方体,如果轮到填充构形中的某一个单位立方体时,我们就必须让它成为某一个积木描述中最靠前(最”小”)的那一部分,因为如果不这样,这个单位立方体就再不可能被覆盖了。这样就免去了上面所说的确定这个单位立方体中相对位置的过程,效率有很大的提高。然而,我们需要一个标准来衡量一个单位立方体的“大小”,这很简单,就先以x为关键字排序;在x相同的前提下,以y为关键字排序;在x,y都相同的前提下,以z为关键字排序。
在程序开始时可以得到一个最优值best=(n – 1) DIV 4 + 1;一旦搜索到的解达到了这个值就可以中止程序。
因为各种积木的第一个单位立方体都是(0,0,0),保存各种状态时只需用三个TPoint,虽然这对于空间存储来说并不是问题。
此外,搜索的顺序对于时间效率很重要,在编程当中,我发现,若把较为平直的积木放在cube数组的前面,这样,循环时先找到的就是较平直的,事实证明,用这样的方法,很多的数据都有明显的超时,这可能是由于平直的积木不利于剪枝而造成的。我们可以同样考虑将积木按体积从达到小排序,这样,即容易出解也利于剪枝。
如下表,给出了示例数据的出解时间:
[时空复杂度]
空间上:有用状态数不是很多,105种,而且目标形状的体积也不是很大,所以空间上是完全承受的了的。
时间上:由于有很多的状态是不能被放置的,所以有了上面的分支定界和剪枝,应该可以在规定时间内出解。
[总结]
做这类题目时应该仔细分析清楚题目,抓住主要矛盾,对这道题来讲就是要仔细分析生成所有状态的方法。
[程序]
预处理程序:
解题程序:
谢谢周旻学长
单位立方体(unit cube)是一个1×1×1的立方体,它的所有角的x,y和z坐标都是整数。我们称两个单位立方体是连通的,当且仅当他们有一个公共的面;一个构形(solid)或者是一个单位立方体,或者是由多个单位立方体组成的连通物体(如图一所示),它的体积就是它所含的单位立方体的个数。
一块积木(block)是一个体积不超过4的构形。如果两块积木可以平移、旋转(注意,不能成镜像)操作变得完全一样,我们就称它们属于同一类。这样,积木总共有12类(如图2所示)。图2中积木的不同颜色仅仅是为了帮助你看清楚它的结构,并没有其他的含义。
一个构形可以由若干块积木组成。如果用一些积木可以互不重叠的搭建出某个构形,那么就称这些积木是这个构形的一种分解(decomposition)。
你的任务是写一个程序,对于给定的一组积木种类和一个构形S,求出境可能少的积木个数,使得他们能够搭建出构形S。你只需给出这个最少的积木个数,以及这些积木各自所属的种类,而不必给出具体的搭建方案。
[输入输出格式]
(1)输入:
在输入文件中,一个单位立方体用同一行中的三个整数x,y,z表示。坐标(x,y,z)是该单位立方体各个顶点坐标{(xi,yi,zi)|i=1..8}中xi+yi+zi最小的。
输入文件有两个。第一个输入文件描述了积木的不同种类,文件为types.in。在下问“输入输出样例”部分给出了一个types.in文件,他一次定义了图2所示的12中积木。注意:所有的测试数据都将统一采用该types.in文件。
每一种类的积木由连续的若干行描述。第一行市积木的种类编号I(1≤I≤12);第二行是该类积木的体积V(1≤V≤4);随后的V行每行包含三个整数x,y和z,表示组成该类积木的一个单位立方体,其中1≤x,y,z≤4。
(2)输出:
输出的文件名为:block.out。
它的第一行是一个正整数M,表示使用积木的最少块数。
文件的第二行列出M个整数,表示搭建出构形S的M块积木各自的种类。
[输入输出样例]
[数据结构]
为描述清楚,先列出数据结构:
TPoint = record {空间中的一个单位立方体} x,y,z: Shortint; end; Tar: array[1..50] of TPoint; {目标形状} TCube = array[1..4] of TPoint; {一个积木} Cube: array[1..most] of TCube; {所有的状态}
[算法分析]
看到这个题目,就会想到搜索。这是一道有一定难度的题目,盲目的搜索是不行的,因为题目中说明了每种积木都可以经过翻转和平移,然而,只要得到了平移或翻转后的积木,我们就只需要一个一个的往构形里面填,直到填满,这显然是一个深度优先搜索。然而对于这道题目来说,除了搜索之外,还有一个难点,就是生成所有的翻转或平移后的积木状态。
我们考虑用一个三员组表示空间中的1*1*1的立方体(单位立方体),三员组(x,y,z)表示空间(x,y,z)处有一个单位立方体。这样我们就有三种数据结构:
TPoint = record{一个点} x,y,z: Shortint; end; TCube = array[1..4] of TPoint;{一个积木} TBlock = array[1..50] of TPoint;{给定的三维形状}
右图所示图形可表示为:
(0,0,0) (0,0,1) (0,1,1)
怎样生成所有状态:
首先,题目中指出:可以平移,翻转,但不可以成镜像。如何处理平移?我们只要保存一个积木中四个顶点的相对位置,在搜索的时候自然就可以完成平移,在此我们规定一个积木的一个单位立方体为(0,0,0),则其余三个可以用相对于这个单位立方体的位置来表示。如何处理翻转?由于题目规定不可以成镜像,所以,我们在生成所有状态时的翻转过程中,必成偶数次镜像(因为:成偶数次镜像等于没成镜像)。由于旋转积木很难理解,也有可能会遗漏一些情况,我们采用的是旋转坐标轴的方法。
可以这么理解旋转坐标轴法:
顾名思义,就是要在三维空间中,将坐标轴旋转,当然,旋转后的坐标要两两垂直,在满足这个条件的情况下,用temp:TCube表示某积木cube Tcube经过某种旋转后所得的状态。设func(temp)表示有几条坐标线(射线)所在直线和原来这条坐标线所在直线的不重合的。考虑func(temp)的不同值:设f(temp)表示在func(temp)的基础上得到的表示temp性质的函数:
对于某一条特定的坐标,如果规定一个方向为正方向,则可以用g(temp)表示旋转后的新坐标线中方向和原来方向不同的条数。(注:这里的方向并非表示三维空间中的方向,只表示正负)。易得:若f(temp)+g(temp)为偶数,则temp是符合题意,即成偶数次镜像的。
搜索的时候,另需一个数组space:array[1..7,1..7,1..7] of byte;
space[x,y,z]=1表示此处未放, space[x,y,z]=0表示放了。
procedure search(u,dep,left: integer); var i: integer; begin if left = 0 then begin 修改;退出; end; 若(left – 1) DIV 4 + 1 >= min then Exit;{分支定界} 找到一个space [tar[u,1],tar[u,2],tar[u,3]]<>0的u;{tar是目标形状} 若不存在则退出; repeat 找到一个可以放置且覆盖(tar[u,1],tar[u,2],tar[u,3])的积木; 若找到则: begin 放下; search(u+1,dep+1,left-count[i]);{count[i]是第I个积木的体积} end; until 找不到; end;
值得注意的是, 找一个可放置的积木时,我们不仅要枚举积木,而且要枚举点(tar[u,1],tar[u,2],tar[u,3])在积木中的位置。如下面的图形:我们要枚举点(tar[u,1],tar[u,2],tar[u,3])在积木中的四中位置情况,然后递归搜索,这样才能够全面。
至此,这道题目可以说是得到比较圆满解决了,按照这种方法便出来的程序速度是比较快的,能够对一半左右的测试数据。
[优化方法]
这里讲的优化,主要是针对搜索时的一些技巧来讲的。
在搜索的过程中,我们发现:我们的目的实际上就是要把一些积木放到构形里面去,不妨换个角度,把一个构形看成由许多单位立方体构成的,同样的,一个积木也是由许多单位立方体构成。我们把这个构形拆开承单个的单位立方体,每次从里面去出成分和某一块积木陈分相同一些单位立方体。因此,不妨令这些单位立方体是有序的,同样,积木的描述也是有序的。搜索时,由小到大去填充构形的某一个单位立方体,如果轮到填充构形中的某一个单位立方体时,我们就必须让它成为某一个积木描述中最靠前(最”小”)的那一部分,因为如果不这样,这个单位立方体就再不可能被覆盖了。这样就免去了上面所说的确定这个单位立方体中相对位置的过程,效率有很大的提高。然而,我们需要一个标准来衡量一个单位立方体的“大小”,这很简单,就先以x为关键字排序;在x相同的前提下,以y为关键字排序;在x,y都相同的前提下,以z为关键字排序。
在程序开始时可以得到一个最优值best=(n – 1) DIV 4 + 1;一旦搜索到的解达到了这个值就可以中止程序。
因为各种积木的第一个单位立方体都是(0,0,0),保存各种状态时只需用三个TPoint,虽然这对于空间存储来说并不是问题。
此外,搜索的顺序对于时间效率很重要,在编程当中,我发现,若把较为平直的积木放在cube数组的前面,这样,循环时先找到的就是较平直的,事实证明,用这样的方法,很多的数据都有明显的超时,这可能是由于平直的积木不利于剪枝而造成的。我们可以同样考虑将积木按体积从达到小排序,这样,即容易出解也利于剪枝。
如下表,给出了示例数据的出解时间:
[时空复杂度]
空间上:有用状态数不是很多,105种,而且目标形状的体积也不是很大,所以空间上是完全承受的了的。
时间上:由于有很多的状态是不能被放置的,所以有了上面的分支定界和剪枝,应该可以在规定时间内出解。
[总结]
做这类题目时应该仔细分析清楚题目,抓住主要矛盾,对这道题来讲就是要仔细分析生成所有状态的方法。
[程序]
预处理程序:
program Preparation; const inputfile = 'types.in'; outputfile = 'kind.out'; Dir: array[1..6, 1..3] of Shortint = {使坐标轴之间产生互换的数组} ((1,3,2),(3,2,1),(2,1,3),(1,2,3),(2,3,1),(3,1,2)); Sign: array[1..8, 1..3] of Shortint = {使坐标轴产生方向变化的数组} ((-1, 1, 1),( 1,-1, 1),( 1, 1,-1),(-1,-1,-1), ( 1, 1, 1),(-1,-1, 1),(-1, 1,-1),( 1,-1,-1)); type TPoint = array[1..3] of Shortint; {一个单位立方体} TCube = record {一个积木} Count: Byte; {体积} Struct: array[1..4] of TPoint; {结构} end; var Cube, Temp: TCube; Cubes: array[1..300] of TCube; {保存所有的积木} Source: array[1..300] of Byte; {这种积木是有第几总积木旋转而来} CubeCount, I, J, K, P, Q, N, V, mX, mY, mZ: Integer; head, tail: integer; function App: Boolean; {判断一种积木是否是重复的} var I, J, P, dX, dY, dZ: Integer; E: TPoint; Same: Boolean; begin App := True; with Temp do begin for i := 1 to Count - 1 do {排序} begin P := i; for j := i to Count do if (Struct[j, 1] < Struct[P, 1]) or (Struct[j, 1] = Struct[P, 1]) and (Struct[j, 2] < Struct[P, 2]) or (Struct[j, 1] = Struct[P, 1]) and (Struct[j, 2] = Struct[P, 2]) and (Struct[j, 3] < Struct[P, 3]) then P := j; E := Struct[i]; Struct[i] := Struct[P]; Struct[P] := E; end; for i := 1 to CubeCount do {判断重复情况} if Cubes[i].Count = Count then begin dX := Cubes[i].Struct[1, 1] - Struct[1, 1]; dY := Cubes[i].Struct[1, 2] - Struct[1, 2]; dZ := Cubes[i].Struct[1, 3] - Struct[1, 3]; Same := True; for j := 1 to Count do if (Cubes[i].Struct[j, 1] - Struct[j, 1] <> dX) or (Cubes[i].Struct[j, 2] - Struct[j, 2] <> dY) or (Cubes[i].Struct[j, 3] - Struct[j, 3] <> dZ) then begin Same := False; Break; end; if Same then Exit; end; end; App := False; end; begin Assign(input, inputfile); Reset(input); for i := 1 to 12 do {读入} begin Readln(N); Readln(V); Cube.Count := 0; for j := 1 to V do with Cube do begin Inc(Count); Readln(Struct[Count, 1], Struct[Count, 2], Struct[Count, 3]); end; for j := V downto 1 do {转化为相对坐标} with Cube do begin Dec(Struct[j, 1], Struct[1, 1]); Dec(Struct[j, 2], Struct[1, 2]); Dec(Struct[j, 3], Struct[1, 3]); end; Temp.Count := V; for k := 1 to 6 do begin if k <= 3 then {分情况旋转坐标} begin head := 1; tail := 4; end else begin head := 5; tail := 8; end; for q := head to tail do begin for j := 1 to V do with Temp do for p := 1 to 3 do Struct[j, p] := Cube.Struct[j, Dir[k, p]] * Sign[q, p]; if not App then {增加一种新的积木} begin Inc(CubeCount); Cubes[CubeCount] := Temp; Source[CubeCount] := N; end; end; end; end; Close(input); Assign(output, outputfile); Rewrite(output); Writeln(CubeCount); {具体输出过程略} Close(output); end.
解题程序:
{$A+,B-,D+,E+,F-,G-,I+,L+,N-,O-,P-,Q-,R-,S-,T-,V+,X+,Y+} {$M 16384,0,655360} program BLOCK; const inputfile = 'block.in'; checkfile = 'block.ans'; outputfile = 'block.out'; Most = 105; type TPoint = array[1..3] of Integer; TCube = array[1..3] of TPoint; const Source: array[1..Most] of Byte = (5,5,5,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,1); Count: array[1..Most] of Byte = (4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1); Cube: array[1..Most] of TCube = (((0,0,1),(0,0,2),(0,0,3)),((0,1,0),(0,2,0),(0,3,0)),((1,0,0),(2,0,0),(3,0,0)), ((0,0,1),(0,1,0),(0,1,1)),((0,1,0),(1,0,0),(1,1,0)),((0,0,1),(1,0,0),(1,0,1)), ((0,0,1),(0,1,0),(0,2,0)),((0,1,0),(0,2,0),(0,2,1)),((0,0,1),(0,1,1),(0,2,1)), ((0,1,0),(0,2,-1),(0,2,0)),((1,0,0),(2,0,0),(2,1,0)),((0,1,0),(1,1,0),(2,1,0)), ((0,1,0),(1,0,0),(2,0,0)),((1,0,0),(2,-1,0),(2,0,0)),((1,0,0),(1,0,1),(1,0,2)), ((0,0,1),(0,0,2),(1,0,0)),((0,0,1),(0,0,2),(1,0,2)),((1,0,-2),(1,0,-1),(1,0,0)), ((0,0,1),(0,0,2),(0,1,0)),((0,1,0),(0,1,1),(0,1,2)),((0,0,1),(0,0,2),(0,1,2)), ((0,1,-2),(0,1,-1),(0,1,0)),((0,1,0),(0,2,0),(1,0,0)),((1,-2,0),(1,-1,0),(1,0,0)), ((1,0,0),(1,1,0),(1,2,0)),((0,1,0),(0,2,0),(1,2,0)),((0,0,1),(1,0,0),(2,0,0)), ((1,0,0),(2,0,0),(2,0,1)),((1,0,0),(2,0,-1),(2,0,0)),((0,0,1),(1,0,1),(2,0,1)), ((0,0,1),(0,0,2),(0,1,1)),((0,1,-1),(0,1,0),(0,1,1)),((1,-1,0),(1,0,0),(1,1,0)), ((0,1,0),(0,2,0),(1,1,0)),((1,0,0),(1,0,1),(2,0,0)),((1,0,-1),(1,0,0),(2,0,0)), ((0,1,0),(0,1,1),(0,2,0)),((0,1,-1),(0,1,0),(0,2,0)),((1,0,0),(1,1,0),(2,0,0)), ((1,-1,0),(1,0,0),(2,0,0)),((0,0,1),(0,0,2),(1,0,1)),((1,0,-1),(1,0,0),(1,0,1)), ((0,0,1),(0,1,-1),(0,1,0)),((0,0,1),(0,1,1),(0,1,2)),((0,1,0),(1,1,0),(1,2,0)), ((0,1,0),(1,-1,0),(1,0,0)),((1,0,0),(1,0,1),(2,0,1)),((1,0,-1),(1,0,0),(2,0,-1)), ((0,1,-1),(0,1,0),(0,2,-1)),((0,1,0),(0,1,1),(0,2,1)),((1,-1,0),(1,0,0),(2,-1,0)), ((1,0,0),(1,1,0),(2,1,0)),((0,0,1),(1,0,-1),(1,0,0)),((0,0,1),(1,0,1),(1,0,2)), ((0,0,1),(0,1,0),(1,0,1)),((1,-1,-1),(1,0,-1),(1,0,0)),((1,0,0),(1,0,1),(1,1,1)), ((0,1,-1),(0,1,0),(1,1,-1)),((1,0,0),(1,1,-1),(1,1,0)),((0,0,1),(0,1,1),(1,1,1)), ((0,1,0),(0,1,1),(1,0,0)),((1,-1,0),(1,-1,1),(1,0,0)),((0,1,0),(1,1,0),(1,1,1)), ((0,0,1),(1,0,0),(1,1,0)),((0,0,1),(1,-1,1),(1,0,1)),((0,1,0),(1,0,-1),(1,0,0)), ((1,0,-1),(1,0,0),(1,1,-1)),((0,1,0),(0,1,1),(1,1,1)),((0,0,1),(0,1,1),(1,0,0)), ((1,-1,1),(1,0,0),(1,0,1)),((1,0,0),(1,1,0),(1,1,1)),((0,0,1),(0,1,0),(1,1,0)), ((0,1,-1),(0,1,0),(1,0,0)),((1,-1,-1),(1,-1,0),(1,0,0)),((0,1,0),(1,0,0),(1,0,1)), ((0,0,1),(1,-1,0),(1,0,0)),((0,0,1),(1,0,1),(1,1,1)),((0,1,0),(1,1,-1),(1,1,0)), ((0,1,-1),(0,1,0),(1,1,0)),((1,0,-1),(1,0,0),(1,1,0)),((1,-1,0),(1,0,0),(1,0,1)), ((0,0,1),(0,1,0),(1,0,0)),((1,-1,0),(1,0,-1),(1,0,0)),((0,0,1),(0,1,1),(1,0,1)), ((0,1,0),(0,1,1),(1,1,0)),((1,0,0),(1,0,1),(1,1,0)),((0,0,1),(0,0,2),(0,0,0)), ((0,1,0),(0,2,0),(0,0,0)),((1,0,0),(2,0,0),(0,0,0)),((0,0,1),(0,1,0),(0,0,0)), ((0,1,0),(0,1,1),(0,0,0)),((0,0,1),(0,1,1),(0,0,0)),((0,1,-1),(0,1,0),(0,0,0)), ((1,0,0),(1,1,0),(0,0,0)),((0,1,0),(1,1,0),(0,0,0)),((0,1,0),(1,0,0),(0,0,0)), ((1,-1,0),(1,0,0),(0,0,0)),((1,0,0),(1,0,1),(0,0,0)),((0,0,1),(1,0,0),(0,0,0)), ((0,0,1),(1,0,1),(0,0,0)),((1,0,-1),(1,0,0),(0,0,0)),((0,0,1),(0,0,0),(0,0,0)), ((0,1,0),(0,0,0),(0,0,0)),((1,0,0),(0,0,0),(0,0,0)),((0,0,0),(0,0,0),(0,0,0))); var F: array[-3..10, -3..10, -3..10] of Byte; {空间中的某一单位立方体是否被覆盖} Tar: array[1..50] of TPoint; {目标构形} Ans, Use: array[1..50] of Integer; {纪录所用的积木种类} N, Left, Min, OK: Integer; E, Ex: TPoint; procedure Initalize; {读入,初始化} var I: Integer; begin Assign(input, inputfile); Reset(input); Readln(N); for i := 1 to N do begin Readln(Tar[i, 1], Tar[i, 2], Tar[i, 3]); F[Tar[i, 1], Tar[i, 2], Tar[i, 3]] := 1; end; Close(input); Min := N + 1; {现在的最优解,及全部用v=1的单位立方体构成需要N个,但为保证有解,就设置为N+1} OK := (N - 1) DIV 4 + 1; {极限最优解} end; procedure Sort(H, T: Integer); {快速排序} var K1, K2: Integer; begin K1 := H; K2 := T; E := Tar[(H + T) DIV 2]; while K1 <= K2 do begin while (Tar[K1, 1] < E[1]) or (Tar[K1, 1] = E[1]) and (Tar[K1, 2] < E[2]) or (Tar[K1, 1] = E[1]) and (Tar[K1, 2] = E[2]) and (Tar[K1, 3] < E[3]) do Inc(K1); while (Tar[K2, 1] > E[1]) or (Tar[K2, 1] = E[1]) and (Tar[K2, 2] > E[2]) or (Tar[K2, 1] = E[1]) and (Tar[K2, 2] = E[2]) and (Tar[K2, 3] > E[3]) do Dec(K2); if K1 <= K2 then begin Ex := Tar[K1]; Tar[K1] := Tar[K2]; Tar[K2] := Ex; Inc(K1); Dec(K2); end; end; if H < K2 then Sort(H, K2); if K1 < T then Sort(K1, T); end; procedure Search(U{现在填第几个单位立方体}, Dep{用了几个积木}, Left{还剩几个单位立方体未翻转}: Byte); var I, x, y, z: Integer; begin if Left = 0 then {已经填完了} begin if Dep < Min then {修改最优解} begin Min := Dep; Ans := Use; end; Exit; end; while (U <= N) and (F[Tar[U, 1], Tar[U, 2], Tar[U, 3]] = 0) do Inc(U); {找到一个未被覆盖的单位立方体} if U > N then Exit; {若不存在则退出} x := Tar[U, 1]; y := Tar[U, 2]; z := Tar[U, 3]; for i := 1 to Most do if (Count[i] <= Left) and (F[x, y, z] = 1) and (F[x + Cube[i, 1, 1], y + Cube[i, 1, 2], z + Cube[i, 1, 3]] = 1) and (F[x + Cube[i, 2, 1], y + Cube[i, 2, 2], z + Cube[i, 2, 3]] = 1) and (F[x + Cube[i, 3, 1], y + Cube[i, 3, 2], z + Cube[i, 3, 3]] = 1) and (Left - Count[i] + 3 < (Min – Dep – 1) * 4) then {是否可以用它填充} begin Inc(Use[Source[i]]); {记录下这个积木的编号} F[x, y, z] := 0; F[x + Cube[i, 1, 1], y + Cube[i, 1, 2], z + Cube[i, 1, 3]] := 0; F[x + Cube[i, 2, 1], y + Cube[i, 2, 2], z + Cube[i, 2, 3]] := 0; F[x + Cube[i, 3, 1], y + Cube[i, 3, 2], z + Cube[i, 3, 3]] := 0; {填充} Search(U + 1, Dep + 1, Left - Count[i]); F[x, y, z] := 1; F[x + Cube[i, 1, 1], y + Cube[i, 1, 2], z + Cube[i, 1, 3]] := 1; F[x + Cube[i, 2, 1], y + Cube[i, 2, 2], z + Cube[i, 2, 3]] := 1; F[x + Cube[i, 3, 1], y + Cube[i, 3, 2], z + Cube[i, 3, 3]] := 1; {恢复填充} Dec(Use[Source[i]]); {删除这个积木的编号} end; end; procedure Print; {打印输出解} var I, J: Integer; begin Assign(output, outputfile); Rewrite(output); Writeln(Min); for i := 1 to 12 do for j := 1 to Ans[i] do Write(i, ' '); Close(output); end; begin Initalize; Sort(1, N); Search(1, 0, N); Print; end.
谢谢周旻学长
相关文章推荐
- Mac OS X 安装protobuf 2.6.1
- win7/8怎么屏蔽win10正式版的安装包提示?
- Hibernate-note03
- 4000 匹配算法·温故知新——「前言」
- PhantomJS:基于WebKit、开源的服务器端JavaScript API
- 硬中断 软中断 信号
- IOS开发-PCH文件的使用
- DLL and PLL
- 博客转移
- PHP中filter函数校验数据的方法详解
- 9.2链表(一)——移除未排序链表中的重复结点
- 关于lib 和 dll 个人学习总结
- KVM中管理存储池(创建和删除)
- git&&github
- jar重新打包
- Swift属性分类
- 即时获取input输入框里的内容 IE/非IE皆可兼容
- 邮箱、手机号、QQ号正则表达式
- 一道逻辑推理题的C++实现
- SFTP和FTS协议的区别