How Big Is It?

来源:互联网 发布:垃圾食品 知乎 编辑:程序博客网 时间:2024/06/04 19:26

Description

Download as PDF


  How Big Is It? 

Ian's going to California, and he has to pack his things, including his collection of circles. Given a set of circles, your program must find the smallest rectangular box in which they fit. All circles must touch the bottom of the box. The figure below shows an acceptable packing for a set of circles (although this may not be the optimal packing for these particular circles). Note that in an ideal packing, each circle should touch at least one other circle (but you probably figured that out).

Input 

The first line of input contains a single positive decimal integer nn<=50. This indicates the number of lines which follow. The subsequent n lines each contain a series of numbers separated by spaces. The first number on each of these lines is a positive integer m,m<=8, which indicates how many other numbers appear on that line. The next m numbers on the line are the radii of the circles which must be packed in a single box. These numbers need not be integers.

Output 

For each data line of input, excluding the first line of input containing n, your program must output the size of the smallest rectangle which can pack the circles. Each case should be output on a separate line by itself, with three places after the decimal point. Do not output leading zeroes unless the number is less than 1, e.g. 0.543.

Sample Input 

33 2.0 1.0 2.04 2.0 2.0 2.0 2.03 2.0 1.0 4.0

Sample Output 

9.65716.00012.657


题目大意:

给定n个大小不等的圆, 要将这n个圆排进一个矩形框中,要求各个圆都要与矩形框的底边相切。 然后从这n各院的所有排列中找出有最小长度的圆排列。如上图所示。


分析与总结:

这题是做的比较纠结的一道题, 求所有的排列很容易,但是要求圆排列的长度却令人纠结,需要考虑的情况比较多。


我的做法是建立一个坐标轴,然后把第一个圆与x轴和y轴相切,然后一个一个圆放入坐标轴中,只需要存好每个圆的圆心

在坐标的坐标即可。


求放入坐标轴后与相连的两个圆的圆心的水平距离:


h = r2-r1;

L = sqrt( (r1+r2)^2 - (r1-r2)^2 );

那么新加入圆的x坐标为上一个相接的那个圆的x坐标+L


下列列出容易出错的问题:

1.  中间的圆很小,而旁边两个圆比较大,中间的被”淹没“了


2. 最前面的那个或最后面的那个圆很小,也被淹没了




最后,还有一个很重要的问题, 与新加入的那个圆相切的那个圆是哪一个呢?

我的做法是,每次新加入一个圆时,先让它与最左边的那个圆相连,然后判断这样放置是否“合理”,如果不行的话,

就与第2个、第3个……最终肯定会找到一个合法的,而且这样的放置一定是最优放置方法。


怎样判断放置是否合理呢?

1.  不管新加入的那个圆的有多么多么的大, 它的圆心横坐标一定是在最右边的,看下图

红线是穿过那些圆的圆心的垂线, 可以看出, 他们圆心的横坐标一定是按照他们的排列顺序来的。

这样就可以判断排除了很多不合法的放置。


2.  计算与其他圆的圆心距离, 如果他们的圆心距离小于他们的半径之和,那么说明他们交叉了,也就是不合法的放置。





#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#define MAXN 20using namespace std;int n, order[MAXN];double arr[MAXN], minVal;bool vis[MAXN];struct Point{    double x, y;};Point coord[MAXN]; // 保存各个圆心在坐标上的位置inline double getDist(Point &a, Point &b){    return sqrt( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}inline void addCircle(int cur, int pos){    for(int i=0; i<cur; ++i){        double l;        if(coord[i].y != arr[pos]){            double h = fabs( coord[i].y - arr[pos] ); // 高            double x = coord[i].y + arr[pos]; // 斜线            l = pow( x*x-h*h, 0.5);        }        else             l = 2*arr[pos];        Point temp;         temp.x = coord[i].x + l,  temp.y = arr[pos];        if(temp.x <= coord[cur-1].x) continue;                 // 判断这种放置是否可以                        bool flag = true;        for(int j=0; j<cur; ++j){            if(i==j) continue;            double t = getDist(coord[j], temp);            if( t < coord[j].y + arr[pos]) {                 flag=false; break;            }        }        if(flag){             if(temp.x < arr[pos]){ // 如果这个圆非常非常大,那么会覆盖掉前面的所有圆                temp.x = arr[pos];            }            coord[cur] = temp;            return;        }        }}void search(int cur, double len){    if(cur==n && len<minVal){         minVal = len;        return;    }    for(int i=0; i<n; ++i)if(!vis[i]){        vis[i] = true;        if(cur==0){             coord[0].x = arr[i], coord[0].y = arr[i];         }        else{            addCircle(cur, i);        }        // 新的长度        double newLen;        if(coord[cur].x + arr[i] > len) newLen = coord[cur].x + arr[i];        else newLen = len;        search(cur+1, newLen);        vis[i] = false;    }}int main(){#ifdef LOCAL    freopen("input.txt","r",stdin);#endif    int T;    scanf("%d", &T);    while(T--){        scanf("%d", &n);        for(int i=0; i<n; ++i)            scanf("%lf", &arr[i]);        minVal = 2147483646; // 注意!一定要赋值一个足够的的数!开始就是因为没有                             // 给一个足够大的数而WA了很多次而发现不出原因        memset(vis, 0, sizeof(vis));        search(0, 0);        printf("%.3lf\n", minVal);    }    return 0;}