(BAPC 2014 Preliminary) C. Floor Painting (动态规划+坐标图形)

The museum of Bizarre Argentinean Pocket Calculators (BAPC) has found a great painter to make a nice floor painting for in the museum. All walls in the museum are straight, and any two adja- cent walls meet at a right angle. All non-adjacent walls are pair- wise disjoint.

Furthermore, the room in which the painting is supposed to come has no pillars. Hence, it is a rectilinear simple polygon.

The design for the painting is square. The museum wantsthe painting to be as large as possible. Furthermore, it should be placed such that the edges of the square are parallel to the walls. Find the maximum possible width for the painting.

输入格式

On the first line one positive number: the number of test cases, at most 100. After that per test case:

  • one line with a single integer nn (4 \leq n \leq 1 0004≤n≤1000): the number of corners in the room.
  • nn lines, each with two space-separated integers xx and yy (0 \leq x, y \leq 100 0000≤x,y≤100000): the coordinates of each corner of the room.

The corners are given in clockwise order.

输出格式

Per test case:

  • one line with a single integer: the maximum width of a square painting that fits on the floor of the museum.

样例输入

3
6
0 8
5 8
5 4
8 4
8 0
0 0
4
0 4
4 4
4 0
0 0
14
4 8
12 8
12 6
14 6
14 8
16 8
16 3
8 3
8 0
0 0
0 6
3 6
3 7
4 7

样例输出

5
4
6

题目来源

BAPC 2014 Preliminary

//这个是角落,一条线段只有两个端点的吧
//最大的思想在于最大的正方形一定是贴着某一right边
//利用水平边的长度来不断找最大的竖直高度来满足这个正方形
#include <algorithm>
#include <cstdio>
using namespace std;

const int nmax = 1000;

const int infty = 1<<30;
enum{right, top, bottom, left}; // Order matters!这个顺序也不是随便来的,右上下左

struct wall                     //wall就是线段的意思吧
{
    int type, x1, x2, y1, y2;
	wall() {}
	wall (int t, int a1, int a2, int b1, int b2) : type(t), x1(a1), x2(a2), y1(b1), y2(b2) {}
};
bool operator < (wall A, wall B)   //left边和水平边总是先处理的
{
    return A.x1 < B.x1 || (A.x1 == B.x1 && A.type < B.type); //顺序就体现在这个type的比较上。在x相同的情况下,right的优先级最高
}                                                            //
bool cmp (wall A, wall B)
{
    return A.y1 < B.y1;
}
int X[nmax], Y[nmax];
wall Wall[nmax], TopWall[nmax/2], BottomWall[nmax/2];       //做个图就知道为什么除以2.
int main()
{
    int runs, run, n, i, j, k, nb, nt, xmin, xblocked, ymin, ymax, dxmax, largestsquare;
	scanf("%d", &runs);
	for (run = 0; run < runs; run++)
	{
		// Read input
		scanf("%d", &n);
		for (i = 0; i < n; i++)        //注意这个坐标系上的点都是>=0的
        scanf("%d %d", &X[i], &Y[i]);  //不必建结构体,直接用X,Y数组存所有的横,纵坐标
		// Organize the walls          //反正就是先到的点先连在一起
		nb = nt = 0;                   //记bottom和top的wall数
		for(i = 0; i < n; i++)
		{
		    j = (i+1) % n;             //x[n-1]还要与x[0]比较,y[n-1]和y[0]比较
			if (X[i] < X[j])           //能先确定水平边再确定竖直边。没有水平边的话在确定竖直边
                                       //顺时针转动的就可以很好地理解这个线段的分类方法
				TopWall[nt++]=Wall[i]=wall(top, X[i], X[j], Y[i], Y[i]);
			else if (X[i] > X[j])
				BottomWall[nb++] = Wall[i] = wall(bottom, X[j], X[i], Y[i], Y[i]);  //这样是每个角落都会有一条线段,上下左右都是以这个角落决定的;
                                                                                    //这里只要注意线段的存储开始的端点总是从x坐标小的开始就行
			else if (Y[i] < Y[j])                                                   //y相同的时候就用x比较,x相同的时候就用y比较
				Wall[i] = wall(left, X[i], X[i], Y[i], Y[j]);                       //Wall存的是水平方向的线段。
			else if (Y[i] > Y[j])
				Wall[i] = wall(right, X[i], X[i], Y[j], Y[i]);                      //同理垂直方向的线段也是y小的端点在上。
		}
		sort(Wall, Wall + n); // Sort by (left) x-coordinate,按排序十分重要
		sort(TopWall, TopWall + nt, cmp); // Sort by y-coordinate
		sort(BottomWall, BottomWall + nb, cmp); // Sort by y-coordinate
		largestsquare = 0;
		i = j = 0;
		while (i < nb && j < nt)
		{
		    //ymin,ymax会随着i,j的改变而改变
		    // BottomWall,TopWall是按y1从小到大排过序的
		    ymin = BottomWall[i].y1;    //md这里y1,y2都相等的吧,没什么区别
			ymax = TopWall[j].y1;       //自然是TopWall的y要大那么些
			xmin = infty;               //x-coordinate of last left wall
			xblocked = -infty;          //Reach of horizontal wall,x所能到达最远的地方
			dxmax = 0;
			for (k = 0; k < n; k++)
			{                                                //注意,这里是不能取等的。
			    if (Wall[k].y1 < ymax && Wall[k].y2 > ymin)  //  就是筛选掉了,两端(max-min)之外包括两端。
					switch (Wall[k].type)                    // 与此同时只保留在max-min这段竖直区间的线段,和相交的竖直线段。
					{
					    case left:
							if (Wall[k].x1 >= xblocked)
								xmin = Wall[k].x1;          //left取最靠近right的left
							break;
						case right:
							if (Wall[k].x1 >= xblocked)    //不是所有right都可取的,right必须在水平边的右边,right每次取最右边的right
								dxmax = max(dxmax, Wall[k].x1 - xmin);
							xmin = infty; // Reset xmin,这重置就意味着,每次只取离left最近的right算dxmax
							break;
						case top: case bottom:   
							xblocked = max(xblocked, Wall[k].x2);
							break;
					}
			}
			if (dxmax <= ymax-ymin)
			{
			    largestsquare = max(largestsquare, dxmax);
				i++;          //i++是使BottomWall上升一段高度,相当于减少高度
			}
			if (ymax-ymin <= dxmax)
			{
			    largestsquare = max(largestsquare, ymax-ymin);
				j++;            //j++是使TopWall上升一段高度,相当于增加高度
			}
		}  //这个也是动态归化吧,从较小的向较大的一步步过渡。
		// Print answer
		printf("%d\n", largestsquare);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xigongdali/article/details/81364144
今日推荐