题目链接: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;
}