首页 > 代码库 > NOJ 1118 玻璃球 【深蓝】[状态压缩DP]
NOJ 1118 玻璃球 【深蓝】[状态压缩DP]
玻璃球 【深蓝】
时间限制(普通/Java) : 10000 MS/ 30000 MS 运行内存限制 : 65536 KByte
总提交 : 57 测试通过 : 11
比赛描述
玩过玻璃球游戏吗?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
最终长度等于第一个球的半径 + 最后一个球的半径 + 中间相邻的球产生的高度。
又是一道状态压缩的题目,dp[status][i]表示的是在选择玻璃球已经放入圆柱管的情况下,最上面的球是第i个球的最小长度
状态转移方程 d[status][i] = d[status ^ (1 << i)][j] – d[j] +d[i] +height(i,j),其中 i 存在于在状态status中,j不存在于status中,height(i,j)相邻着放所产生的长度。
#include <iostream> #include <stdio.h> #include <math.h> #include <string.h> using namespace std; double d; int n; double r[20]; double calheight(int i, int j) { double l = r[i] + r[j]; double a = d - l; return sqrt(l*l - a*a); } double dp[1 << 16 |1][16]; //状态status下 i在最上面的最小值 //dp[status][j]=dp[status ^ (1 << j)][i] - r[i] + cal(i,j) + r[j] ; bool read() { scanf("%d%lf", &n, &d); if(n == 0 && d == 0) return false; for(int i = 0; i < n; i++) scanf("%lf", &r[i]); return true; } int solve() { memset(dp, 0, sizeof(dp)); for(int s = 0; s < (1 << n); s++) { for(int i = 0; i < n; i++) { if(s & (1<<i)) continue; if(s) dp[s ^ (1<<i)][i] = 1000000000; else dp[s ^ (1<<i)][i] = r[i] + r[i]; for(int j = 0; j < n; j++) { if(s & (1<<j)) { dp[s ^ (1 << i)][i] = min(dp[s ^ (1<<i)][i], dp[s][j] - r[j] + r[i] + calheight(i,j)); } } } } double ans = 1000000000; for(int i = 0; i < n; i ++) { ans =min(ans, dp[((1 << n) - 1)][i]); } printf("%.0f\n", (ans)); } int main() { while(read()) { solve(); } return 0; }
NOJ 1118 玻璃球 【深蓝】[状态压缩DP]