码农的空间

codding
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

解决Rectangle Packing问题【原创】

Posted on 2011-07-07 16:40  我是孙海龙  阅读(3130)  评论(3编辑  收藏  举报

问题描述:如何把任意数量任意尺寸矩形集无重复的放到一个面积最小的封闭矩形中。

算法思想:(为了便于描述,把要找的封闭矩形记为a,封闭矩形的集合记为as,把矩形集合记为rs,n为rs中矩形的个数,把可以插入矩形的位置记为corners)

1.把所有矩形集中的矩形按高度从大到小排序,此时rs[0]高度最大
2.把a初始化为:height = rs[0].height,width = rs[0].width + rs[1].width + ...... + rs[n - 1].width,corners初始化为:坐标顶点
3.把rs[0]放入a中,并把由于rs[0]的插入产生的corner放入corners集合中,删除已经使用的corner,如下图所示:

4.从rs[1]到rs[n - 1],每次插入时选择可以插入的X值最小的corner,插入成功之后,删除已经使用的corner,并且插入新产生的corner,由于a的初始化条件,可以保证rs中的所有矩形都可以插入a中
5.所有矩形插入完成后,对a进行“瘦身”,让a正好包含所有的矩形,并记录此时a的宽度为width,a的面积为area(此时a是所有符合条件的封闭矩形中的‘最狭长’的,令as[0] = a)
6.依次减少a.width,增加a.height,重复3-5,如果a的面积有所减少,则把a插入到as中,直到a.width = rs中最宽矩形的宽度
7.as集合的最后一个元素就是可以包含rs中所有矩形的面积最小的封闭矩形,算法结束

算法实现:

在算法的实现中需要注意的地方:
1.计算由于矩形的插入产生的新的corner(需要考虑很多特殊情况)
2.判断矩形是否可以插入一个corner
3.数据结构的设计

以下程序是算法最核心的方法,插入矩形,并记录新产生的corner

	private boolean putIn(PngRect png){
		if(pngs == null){
			pngs = new ArrayList<PngRect>();
			pngs.add(png);
			png.setStartP(new Point(0,0));
			if(png.getHeight() < mHeight){
				Rect owner = new Rect(0,png.getStopP().getY(),mWidth,mHeight);
				Corner corner = new Corner(new Point(0,png.getHeight()),owner);
				corners.add(corner);
			}
			if(png.getWidth() < mWidth){
				Rect owner = new Rect(png.getStopP().getX(),0,mWidth,mHeight);
				Corner corner = new Corner(new Point(png.getWidth(),0),owner);
				corners.add(corner);
			}
			return true;
		}else{
			Corner corner;
			int x;
			int y;
			Rect owner;
			for(int i = 0; i < corners.size(); ++i){
				corner = corners.get(i);
				x = corner.getPoint().getX();
				y = corner.getPoint().getY();
				owner = corner.getRect();
				Point startP = corner.getPoint();
				int endX = owner.right;
				int endY = owner.bottom;
				/*
				 * 可以放进该corner,此处存在覆盖问题,需要判断四条边是否有点在已经被使用,如果是,则返回,试下一个corner
				 * v0-----v1
				 * |       |
				 * |   a   |
				 * |       |
				 * v2-----v3
				 * 同时为了防止完全重叠,还需要判断额外一个点,例如图中的a点
				 */
				if(x + png.getWidth() <= mWidth && y + png.getHeight() <= mHeight){
					Point v0 = startP;
					Point v1 = new Point(startP.getX() + png.getWidth(),startP.getY());
					Point v2 = new Point(startP.getX(),startP.getY() + png.getHeight());
					Point v3 = new Point(startP.getX() + png.getWidth(),startP.getY() + png.getHeight());
					Point a = new Point(startP.getX() + png.getWidth()/2,startP.getY() + png.getHeight()/2);
					if(contains(v0,v1,true) || contains(v1,v3,false) || contains(v2,v3,true) || contains(v0,v2,false) || contains(a)){//有可能正好落在边上
						continue;
					}
//					if(contains(a) ||contains(v0) || contains(v1) || contains(v2) || contains(v3) ){//有可能正好落在边上
//						continue;
//					}
					if(x + png.getWidth() < endX){
						corners.add(i + 1,new Corner(new Point(x + png.getWidth(),y),
								new Rect(x + png.getWidth(),y,mWidth,mHeight)));//此处owner可能为空
					}
					if(y + png.getHeight() < endY){
						corners.add(i + 1, new Corner(new Point(x,y + png.getHeight()),
								new Rect(x,y + png.getHeight(),mWidth,mHeight)));
					}
					corners.remove(i);
					sortCorners();
					png.setStartP(corner.getPoint());
					pngs.add(png);
					return true;
				}
			}
		}
		return false;
	}

算法实现的效果如下所示:

如果矩形集个数比较多,算法执行时间较长,应该可以有优化的地方。