题意:让你求出能放下所有圆的最小矩形,刚开始是单纯的全排列然后在计算,找出最小值,但后来发现会有圆与圆相交的情况,所以我们必须保证当前要放置的圆的位置是与之前的所有圆相切所确定的位置的最大值,至于计算位置的方法就很简单了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int vis[8],num;
double a[8],b[8],d[8],_max;
void check(int cur) //令当前放置的圆与前面所有的圆相切求出最大的据原点的距离(这样就可以保证排除圆圆相交的情况),再加上前面的距离即为放置这个新的圆的位置坐标。
{
d[cur] = b[cur];
double dis1,dis2,dis;
for (int i = cur - 1 ; i >= 0 ; i--)
{
dis1 = b[i] + b[cur];
dis2 = b[i] - b[cur];
dis = d[i] + sqrt(dis1*dis1-dis2*dis2);
if (dis > d[cur])
d[cur] = dis ;
}
}
void dfs(int cur,double sum)
{
if (sum > _max)
return ;
if (cur == num)
{
double l = b[0];
sum += b[cur-1]; // 先确定可能最小的总长,就是最后的圆心坐标加上它的半径
for(int i = 0 ; i < num - 1; i++) // 可能的一种的情况是一个超大的圆,然后接着几个超小的圆
if(d[i] + b[i] > sum)
sum = d[i] + b[i];
for(int i = 1; i < num; i++) // 可能的情况是一个超大的圆之前连着几个超小的圆,所以就不能直接加上第一个圆的半径了,要找出最大的值
if(b[i] - d[i] > l)
l = b[i] - d[i];
sum += l;
if(sum < _max)
_max = sum ;
return ;
}
for (int i = 0; i < num; i++)
if (!vis[i])
{
vis[i] = 1;
b[cur] = a[i];
check(cur);
dfs(cur+1,d[cur]);
vis[i] = 0 ;
}
}
int main()
{
int n;
scanf("%d",&n);
memset(vis,0,sizeof(vis));
while (n--)
{
_max = 12345567777;
scanf("%d",&num);
for (int i = 0; i < num; i++)
scanf("%lf",&a[i]);
for (int i = 0; i < num; i++)
{
vis[i] = 1;
d[0] = 0 ; //圆心的坐标
b[0] = a[i];
dfs(1,0);
vis[i] = 0 ;
}
printf("%.3lf\n", _max);
}
return 0;
}