玻璃球 【深蓝】
时间限制(普通/Java) : 10000 MS/ 30000 MS 运行内存限制 : 65536 KByte
总提交 : 47 测试通过 : 10
总提交 : 47 测试通过 : 10
比赛描述
玩过玻璃球游戏吗?sed同学小学没有毕业之前玩过,他常常用一个直径为d的圆柱管来装玻璃球,已知每个玻璃球是半径为 r1, r2, . . . , rn的球体,他当时一直有一个疑问:装这些玻璃球最少需要多长的圆柱管。现请你帮他解决这个问题。
假设玻璃球半径充分大,以至于管中不存在三个可以同时相互接触的玻璃球。给定这样的限制,表示玻璃球将总以这样的方式装入:他们的中心点位于含导管旋转轴的2维平面上。
输入
输入包含多个测试例。每个测试例包括两行。每个测试例的第一行包含一个整数n (1 ≤ n ≤ 15) ,表示sed持有玻璃球的数目,还有一个浮点值d (2.0 ≤ d ≤ 1000.0) ,表示管子直径。两值以空格隔开。每个测试例的第二行包含n个由空格分隔的浮点数r1 r2 . . . rn(1.0 ≤ ri ≤ d/2) ,为sed玻璃球的半径。一个空白行分隔所以输入测试例。一行“0 0” 表示输入结束,无需处理此例。
输出
对于每个输入测试例,打印管子的最短长度,结果取整。
样例输入
2 98.1789
42.8602 28.7622
3 747.702
339.687 191.953 330.811
0 0
样例输出
138
1628
题目来源
“IBM南邮杯”团队赛2009
//玻璃球 【深蓝】——集合DP
#include<iostream>
#include<cmath>
using namespace std;
/*
int main()
{
double r1 = 42.8602, r2 = 28.7622, d = 98.1789;
double ans;
ans = r1 + r2 + sqrt((r1+r2)*(r1+r2) - (d-r1-r2)*(d-r1-r2));
printf("%f\n",ans);
return 0;
}
*/
double dp[1<<15][15];
bool vis[1<<15];
int n;
double d, r[15];
double cal(double r1, double r2)
{
return sqrt((r1+r2)*(r1+r2) - (d-r1-r2)*(d-r1-r2));
}
void dfs(int cur) // !!!
{
int t = 1;
for(int i=0;i<n;i++)
{
if(cur&t)
{
if(cur^t)
{
if(!vis[cur^t]) dfs(cur^t);
}
else
{
dp[cur][i] = 2 * r[i];
return;
}
vis[cur^t] = true;
for(int j=0;j<n;j++)
dp[cur][i] = min(dp[cur][i], dp[cur^t][j]+cal(r[j], r[i])-r[j]+r[i]);
}
t = t<<1;
}
}
int main()
{
while(scanf("%d%lf",&n,&d) && n)
{
for(int i=0;i<n;i++)
scanf("%lf",&r[i]);
int m = 1<<n; // 2^n
memset(vis, false, sizeof(vis));
//dp[i][j]表示j个球需要的管子的最短长度
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
dp[i][j] = 15000; // d<=1000.0
dfs(m-1); // 最大的
double ans = 1e9;
for(int i=0;i<n;i++)
if(ans > dp[m-1][i])
ans = dp[m-1][i];
printf("%d\n",(int)(ans+0.5)); // 四舍五入,以前写的好麻烦!int不能省
}
return 0;
}