uva 10012 - How Big Is It?(排列 + 回溯)

来源:互联网 发布:windows xp升级win7 编辑:程序博客网 时间:2024/06/13 01:49

题目:uva 10012 - How Big Is It?


题目大意:给出n个圆的半径,要求圆要和地面相切,求这样的圆怎么组合能够使得盒子的宽度最短。


解题思路:这题里面情况会有两个大圆相切,然后中间夹多个小圆,这样这些小圆的距离就不用算入盒子的宽度了,这里先用排列组合的方式将这几个圆全部的组合方式排列出来,这样这些圆的前后关系就定了,但是现在就需要判断这样的情况下的盒子的宽度,这时候就需要考虑是否有上述的那种情况,所以就需要定一下每个圆圆心的位置,每次把要排列的圆与它的前一个相切,但是这样也会出现这个圆有可能和前面的圆(前一个圆的前面的圆)相交,这样是不可以,就需要将这个圆圆心的位置改为和前面的与他相交的那个圆相切的状况的位置,这样一一和前面已经排好的每一个圆判断是否相交,相交就移到和他们相切的位置。这样就是每一次排列的盒子的宽度,最后取最小值。


代码:

#include <stdio.h>#include <string.h>#include <math.h>#include <algorithm>using namespace std;const int N = 10;const double INF = 0x6ffffff;double r[N], front[N], tail[N], ans;int n;void init () {scanf("%d", &n);for (int i = 0; i < n; i++)scanf ("%lf", &r[i]);sort(r, r + n);ans = INF;}bool cmp (double x, double y) {  //判断x< yreturn x - y < -1e-9;}double dis (int a, int b) {  //两圆相切之间的距离return 2 * sqrt (r[a] * r[b]);}double len (int a, int b) {  //两圆相切球心之间的距离return sqrt (pow (front[a] - front[b], 2) + pow (r[a] - r[b], 2));}void solve () {double sum = 0;memset(front, 0, sizeof(front));memset(tail, 0, sizeof(tail));front[0] = tail[0] = r[0];sum = 2 * r[0];int i, j;for (i = 1; i < n; i++) {front[i] = front[i - 1] + dis(i, i - 1);tail[i] = r[i];if (cmp (front[i], r[i]))              //控制第一个圆至少和盒子的左边缘相切,不能超出左边缘front[i] = r[i];for (j = 0; j < i - 1; j++)             //判断是否当前位置的这个圆和之前的圆是否相交,相交就将这个圆的位置更新到与那个相交的圆相切的位置if ( cmp (len (j, i) , r[j] + r[i]))front[i] = front[j] + dis (i, j);if (cmp (sum, front[i] + r[i]))         //每次排好第i个圆后都有宽度了,维护最大值sum = front[i] + r[i];}if (cmp(sum, ans))                              //取最小的总宽度值ans = sum;}int main () {int t;scanf ("%d", &t);while (t--) {init();do {solve ();}while (next_permutation(r, r + n));printf("%.3lf\n", ans);}return 0;}


0 0
原创粉丝点击