uva 10012How Big Is It?

来源:互联网 发布:安卓魔音软件 编辑:程序博客网 时间:2024/06/05 23:58

原题:
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 n, n ≤ 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
3
3 2.0 1.0 2.0
4 2.0 2.0 2.0 2.0
3 2.0 1.0 4.0
Sample Output
9.657
16.000
12.657

中文:
有个人要去旅行要带一堆圆环,现在给出你这些圆环的半径,问你如何摆放,使得需要的箱子长度最小?

#include<bits/stdc++.h>using namespace std;typedef long long ll;const double inf=99999999;double circle[9],ans,tmp,X[9];int n;double my_abs(double x){    if(x<0)        return -x;    return x;}double get_dis(int c1,int c2){    double a=circle[c1]+circle[c2];    double b=my_abs(circle[c1]-circle[c2]);    return sqrt(a*a-b*b);}//与第i个圆相切的圆一定是x坐标最远的那个void solve(){    do    {        memset(X,-1,sizeof(X));        X[1]=circle[1];        tmp=-1;        for(int i=2;i<=n;i++)        {            double x_=circle[i];            for(int j=1;j<i;j++)//找最大的                x_=max(x_,X[j]+get_dis(i,j));            X[i]=x_;        }//        cout<<endl;//        for(int i=1;i<=n;i++)//            cout<<X[i]<<" ";//        cout<<endl;//        for(int i=1;i<=n;i++)//            cout<<circle[i]<<" ";//        cout<<endl;        for(int i=1;i<=n;i++)            tmp=max(tmp,X[i]+circle[i]);//        cout<<fixed<<setprecision(3)<<tmp<<endl;        ans=min(ans,tmp);    }    while(next_permutation(circle+1,circle+1+n));}int main(){    ios::sync_with_stdio(false);    int t;    cin>>t;    while(t--)    {        cin>>n;        for(int i=1;i<=n;i++)            cin>>circle[i];        ans=inf;        sort(circle+1,circle+1+n);        solve();        cout<<fixed<<setprecision(3)<<ans<<endl;    }    return 0;}//3 8 1 2//

解:
刚一看这题觉得挺简单的,直接搜索或者用生成排列呗。结果在纸上一算,发现有好多坑。

首先,最简单的情况,如果圆的半径相差不大,那么直接顺序排列就行了。
这里写图片描述

第二种,有半径相差比较大的圆,那么小的那个圆可以塞到大圆与箱底的那个“缝隙”当中。
这里写图片描述

第三种,如果有半径相差比较大的圆,小圆那个圆”靠墙”,那么大的那个圆也要”靠墙”。
这里写图片描述

考虑这三种情况之后,可以很容易的想到暴力+贪心的策略来解决,也就是每次枚举圆的排列顺序,然后把圆尽量贴到它最左侧的圆环。但是由于数据给出的是半径,而不是坐标,所以要把之前排列好的圆用坐标的方式表达出来。

每个圆的纵坐标都固定是它的半径,横坐标要算出用到它前面那个圆的横坐标,然后再加上当前圆和它前面那个圆的圆心的水平距离。

这里写图片描述
在代码中get_dis(c1,c2)表示计算第c1个圆和第c2个圆之间的水平距离。X[]数组记录圆的横坐标。考虑第i圆如何选择,与之前所有摆放好的圆环进行相切,然后记录下所有的横坐标距离,找出最大的那个横坐标,就是第i个圆应该摆放的位置。如图
这里写图片描述
黑色的圆是之前摆放好的,第i个圆分别与前面的3个黑圆相切处理,找到最靠右,也就是横坐标最大的那个就是应该摆放的位置(如图的红圈就是)

此题也可以剪枝处理,当然,此时要用深搜,如果摆放的一些的圆环还没有摆放完就发现比之前找到的解还要大,就可以剪枝了。

0 0
原创粉丝点击