AtCoder Beginner Contest 274E - Booster

题目链接E - Booster

题意:一个二维平面有n个城镇,m个加速点,初始v = 1,每经过一个加速点速度就可以变大一倍,初始在源点,求经过n个城镇且最后回到源点的最短时间。1<=N<=12, 0<=M<=5

分析:

题意加上数据范围告诉我们这是一个很经典的“旅行商问题”,不过多了一个速度。

不妨设f[S][i]表示当前经过点的集合为S且停在i的最短时间, 那么f[s][i] = (f[p][j]+dis(j,i)/v, f[s][i])

dis(i, j) = 两点间距离,v则是p集合中加速点的个数

代码:

#include <bits/stdc++.h>
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define x first
#define y second
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
const int M = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const double INFF = 0x7f7f7f7f7f7f7f7f;
const int mod = 1e9 + 7;
int n, m;
PII op[N];
double f[1 << (18)][20];

double dis(PII a, PII b)
{
    return sqrt(1.0 * (a.y - b.y) * (a.y - b.y) + (a.x - b.x) * (a.x - b.x));
}

int count(int x)
{
    int ans = 0;
    for (int i = n; i < n + m; i++)
    {
        if (x >> i & 1)
            ans++;
    }
    return ans;
}

int get(int x)
{
    int res = 1;
    for (int i = 1; i <= x; i++)
        res *= 2;
    return res;
}

signed main()
{

    cin >> n >> m;
    op[0] = {0, 0};
    n++;
    for (int i = 1; i < n + m; i++)
    {
        double a, b;
        cin >> a >> b;
        op[i] = {a, b};
    }

    for (int i = 0; i < 1 << (n + m); i++)
    {
        for (int j = 0; j < n + m; j++)
        {
            f[i][j] = 1e18;
        }
    }

    f[1][0] = 0;

    for (int i = 1; i < 1 << (n + m); i++)
    {
        for (int j = 0; j < n + m; j++)
        {
            if (i >> j & 1)
            {
                for (int k = 0; k < n + m; k++)
                {
                    if (k == j)
                        continue;
                    if (i >> k & 1)
                    {
                        int state = i - (1 << j);
                        int num = count(state);
                        int v = get(num);
                        double dist = dis(op[j], op[k]);
                        f[i][j] = min(f[state][k] + 1.0 * dist / v, f[i][j]);
                    }
                }
            }
        }
    }

    double tmin = INFF;
    for (int i = (1 << (n)) - 1; i < (1 << (n + m)); i += 1 << n)
    {
        for (int j = 0; j < n + m; j++)
            tmin = min(tmin, f[i][j] + dis(op[0], op[j]) / get(count(i)));
    }

    printf("%.10lf\n", tmin);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值