CF1523F Favorite Game(状压dp)
题目:洛谷CF1523F、CF1523F
题目描述:
初始选择平面内一个位置,每次花费\(1\)时间代价向上下左右这\(4\)个方向中选择一个方向前进一个单位长度,你也可以选择不动
有\(n\)座塔,第\(i\)座的坐标为\((xa_i,ya_i)\),只要你到达过\((xa_i,ya_i)\)后,不管你后来在哪个位置,你均可以通过\(0\)时间花费直接传送到位置\((xa_i,ya_i)\)
有\(m\)个任务,每个任务为\((xb_i,yb_i,t_i)\),如果你在第\(t_i\)时间时位于坐标\((xb_i,yb_i)\),那么你就可以完成这个任务
求最多能完成多少个任务
\(n \leq 14\),\(m \leq 100\),\(xa_i,ya_i,xb_i,yb_i \leq 10^6\),\(t_i \leq 10^9\)
蒟蒻题解:
观察数据范围,\(n \leq 14\),对于每个塔只需要知道它有没有被遍历过,很容易想到把经过塔的状态压成一个二进制数
由于\(t \leq 1e9\),很大,显然不能用它来当状态
在移动过程中,有用的位置显然就只有塔的位置和任务的位置,对于任务只需要知道当前任务对应的时间它在不在这个位置,所以有用的时间只有任务对应的时间和经过塔的时间
设\(dp\)方程\(f_{i,j}\),表示当前遍历过的塔的状态为\(i\),当前完成了任务\(j\),当前的时间为\(t_j\),坐标在\((xb_j,yb_j)\),最多能完成多少个任务
发现中间从任务走到塔以及中间塔走到塔,塔走到任务很难转移,尝试引入新的\(dp\)
由于要让答案最大,如果当前确定了遍历过哪些塔,且最后一步是在塔中的话(不是在塔中的话就是刚完成了一个任务,即上面的\(f\)),我们接下去的目标要么是走到一座新的塔中,要么就是走到一个任务,要完成某个任务,且前面塔的状态已知了,只需要知道能不能在合法时间内走到这个任务即可,如果时间越小肯定越优
所以设\(dp\)方程\(g_{i,j}\)表示当前遍历过的塔的状态为\(i\),当前完成了\(j\)个任务,且当前的最后位置是在塔中所需要的最小时间
这样的话\(dp\)方程就很好推了,此处留给读者思考
时间复杂度\(\mathcal O(2^nm(n+m))\)
参考程序:
#include<bits/stdc++.h>
using namespace std;
#define Re register int
const int N = 16500, M = 105, Inf = 1e9 + 5;
struct info
{
int x, y, t;
}a[18], b[M];
int n, m, nn, ans, dit[N][18], dis[N][M], f[N][M], g[N][M];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline bool cmp(info x, info y)
{
return x.t < y.t;
}
inline int max(int x, int y)
{
return x > y ? x : y;
}
inline int min(int x, int y)
{
return x < y ? x : y;
}
int main()
{
n = read(), m = read(), nn = 1 << n;
for (Re i = 0; i < n; ++i) a[i].x = read(), a[i].y = read();
for (Re i = 1; i <= m; ++i) b[i].x = read(), b[i].y = read(), b[i].t = read();
sort(b + 1, b + m + 1, cmp);
for (Re i = 0; i < nn; ++i)
for (Re j = 0; j <= m; ++j) g[i][j] = Inf;
for (Re i = 0; i < nn; ++i)
{
int u;
for (Re j = 0; j < n; ++j)
if ((i >> j) & 1)
{
u = j;
for (Re k = 0; k <= m; ++k) g[i][k] = min(g[i][k], g[i ^ (1 << j)][k] + dit[i ^ (1 << j)][j]);
}
if (!i)
{
g[0][0] = 0;
for (Re j = 0; j < n; ++j) dit[0][j] = Inf;
for (Re j = 1; j <= m; ++j) dis[0][j] = Inf, f[0][j] = 1;
}
else if (i == (1 << u))
{
g[i][0] = 0;
for (Re j = 0; j < n; ++j) dit[i][j] = abs(a[u].x - a[j].x) + abs(a[u].y - a[j].y);
for (Re j = 1; j <= m; ++j) dis[i][j] = abs(a[u].x - b[j].x) + abs(a[u].y - b[j].y);
}
else
{
int v = i ^ (1 << u);
for (Re j = 0; j < n; ++j) dit[i][j] = min(dit[v][j], dit[1 << u][j]);
for (Re j = 1; j <= m; ++j) dis[i][j] = min(dis[v][j], dis[1 << u][j]);
}
for (Re j = 1; j <= m; ++j)
{
for (Re k = j - 1; k >= f[i][j]; --k)
if (g[i][k] + dis[i][j] <= b[j].t) f[i][j] = k + 1;
if (!f[i][j]) continue;
for (Re k = j + 1; k <= m; ++k)
if (b[j].t + min(dis[i][k], abs(b[k].x - b[j].x) + abs(b[k].y - b[j].y)) <= b[k].t) f[i][k] = max(f[i][k], f[i][j] + 1);
g[i][f[i][j]] = min(g[i][f[i][j]], b[j].t), ans = max(ans, f[i][j]);
for (Re k = 0; k < n; ++k)
if (!((i >> k) & 1)) g[i | (1 << k)][f[i][j]] = min(g[i | (1 << k)][f[i][j]], b[j].t + min(dit[i][k], abs(a[k].x - b[j].x) + abs(a[k].y - b[j].y)));
}
}
printf("%d", ans);
return 0;
}

浙公网安备 33010602011771号