首页 > 代码库 > 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]