嘘~ 正在从服务器偷取页面 . . .

洛谷P3638 [APIO2013] 机器人 题解


洛谷P3638 [APIO2013] 机器人 题解

题目链接:P3638 [APIO2013] 机器人

题意

VRI(Voltron 机器人学会)的工程师建造了 $n$ 个机器人。任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人。

我们把机器人用 $1$ 至 $n$ 编号。如果两个机器人的编号是连续的,那么它们是兼容的,可以合并成一个复合机器人。最初这 $n$ 个机器人各自都只有唯 一的编号。而一个由两个或以上的机器人合并构成的复合机器人拥有两个编号, 分别是构成它的所有机器人中最小和最大的编号。

例如,$2$ 号机器人只可以与 $1$ 号或 $3$ 号机器人合并。若 $2$ 号机器人与 $3$ 号机器人合并,可构成编号为 $2\sim 3$ 的复合机器人。如果编号为 $2\sim 3$ 的复合机器人与编号为 $4\sim 6$ 的复合机器人合并,可构成编号为 $2\sim 6$ 的复合机器人。当所有机器人合 并以后则构成 $1\sim n$ 复合机器人。

工程师把这 $n$ 个机器人放在了一个封闭的房间中,房间四周均是墙。该房间 被划分成 $w \times h$ 个方格。有些方格有障碍物,机器人不可经过或停留;其余方格允许多个机器人停留,同时允许机器人经过。任何时候一个机器人只占用一个方格。初始时刻,所有机器人均在不同的方格中。

这些原始的机器人不会自发地移动。它们只有被工程师沿 $x$ 轴或 $y$ 轴推动 后,才会沿推动的方向不断向前直线移动,直至碰到障碍物或墙停止移动。停止移动后,它会扫描当前的格子是否存在可以与它合并的机器人,如果有,则合并并继续检查,直至不能再合并为止。工程师只能沿水平向左、水平向右、竖直向 上、竖直向下四个方向推动机器人,并且,在机器人尚未停止移动时,不允许推动其它机器人,因此任何时刻,房间中都只能有一个机器人移动。

为了帮助机器人转向,工程师在一些格子中放置了转向器。具体地说,转向 器分为顺时针转向器(右转器)和逆时针转向器(左转器),顺时针转向器可以 使到达该格子的机器人沿顺时针方向转向 90°;逆时针转向器可以使到达该格 子的机器人沿逆时针方向转向 90°。

现在,我们将告诉你初始时刻房间内的信息。请你计算工程师最少共计需要 推动机器人多少次,才能把所有的 n 个机器人全部合并(如果可能的话)。

输入格式

输入的第 $1$ 行包含 $3$ 个整数 $n,w$ 和 $h$,用 空格隔开。

输入文件中接下来的 $h$ 行描述初始时刻房间内的信息,每行包含 $w$ 个字符。 这 $w\times h$ 个字符中每一个表示房间中的一个格子,意义如下:

  • 19 :表示该方格中有一个机器人,编号为这个数字;
  • x :表示该方格有障碍物;
  • A :表示该方格中有一个逆时针转向器;
  • C :表示该方格中有一个顺时针转向器;
  • . 表示该方格为空地。

输出格式

输出仅一个整数,表示最少需要推动的次数。若不能使所有机器人全部合并,输出 $-1$ 。

数据范围

$1\le n \le 9, 1 \le w,h\le 500$

从合并方式可以看出,这很可能是一个区间dp。

考虑设 $f_{i,j,x,y}$ 表示 $i \sim j$ 号机器人合并到 $(x,y)$ 所需的最少次数。

考虑转移,首先是简单的合并(这里 $a\downarrow b + c$ 表示 $a \gets \min\{a,~b+c\}$ )

然后是走路到的,设 $s(d,x,y)$ 表示 $(x,y)$ 往 $d$ 方向移动能到达的坐标,则

接着考虑转移顺序。第一部分只要简单的区间dp就可以了,第二部分就是斯坦纳树。

换句话说,由于我们可以由 $(x,y)$ 更新 $s(d,x,y)$ ,则我们可以把第二个式子看作三角不等式,即

在外层枚举区间 $[i,j]$ 时,我们先转移合并的答案,然后跑一个 01bfs 就好了

时间复杂度 $\mathcal{O}(n^3wh)$

代码:(有些人懒得自己写,我不说是谁

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N 10
#define M 525
#define L ((int)(4e6 + 15))
typedef int mat[M][M], (*pmat)[M];
typedef pair<int,int> pii;
#define Fi first
#define Se second
const int dx[4] = {0,-1,0,1}, dy[4] = {-1,0,1,0};

char mp[M][M]; mat hsh[4], f[N][N]; pmat ptr;
int n,W,H,ans=INF,hcnt,tcnt; pii ro[N], dest[4][M][M], que[L], fy[M * M]; 
bool cmp(pii x, pii y) { return ptr[x.Fi][x.Se] < ptr[y.Fi][y.Se]; }
int nxt(int x) { return (++x) == L ? 0 : x; }
int prv(int x) { return (x ? x : L) - 1; }
pii dfs(int x,int y,int d)
{
    pii &ret = dest[d][x][y]; if(ret.Se) return ret; if(hsh[d][x][y] == hcnt) return ret = pii(-1, -1); 
    hsh[d][x][y] = hcnt; int k = (unsigned)mp[x][y] - 64ull < 4ull ? (d - mp[x][y]) & 3 : d; 
    int tx = x + dx[k], ty = y + dy[k];
    return ret = (mp[tx][ty] == 'x' ? pii(x,y) : dfs(tx, ty, k));
}
void bfs(pmat dist, int tot)
{
    int h = 0, t = 0, i = 0, d; pii u,v;
    for(; i < tot || h != t; h = nxt(h))
    {
        u = que[h];
        if(h == t || (i < tot && dist[fy[i].Fi][fy[i].Se] < dist[u.Fi][u.Se]))
        { que[h = prv(h)] = u = fy[i++]; }
        for(d = 0; d < 4; d++)
        {
            v = dest[d][u.Fi][u.Se];
            if((~v.Fi) && dist[v.Fi][v.Se] > dist[u.Fi][u.Se] + 1)
            { dist[v.Fi][v.Se] = dist[u.Fi][u.Se] + 1; que[t] = v; t = nxt(t); }
        }
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> W >> H; pmat f1, f2; int id;
    memset(mp, 'x', sizeof(mp)); memset(f, 0x3f, sizeof(f)); 
    for(int i = 1; i <= H; i++)
    {
        cin >> (mp[i] + 1); mp[i][W + 1] = 'x';
        for(int j = 1; j <= W; j++) if(isdigit(mp[i][j]))
        { id = mp[i][j] & 15; ro[id] = pii(i,j); f[id][id][i][j] = 0; }
    }
    for(int i = 1; i <= H; i++)
        for(int j = 1; j <= W; j++) if(mp[i][j] != 'x')
            for(int d = 0; d < 4; d++) { ++hcnt; dest[d][i][j] = dfs(i,j,d); }
    for(int i = 1; i <= n; i++) { fy[0] = ro[i], bfs(f[i][i], 1); }
    for(int d = 1; d < n; d++)
        for(int i = 1, j; i <= n - d; i++)
        {
            j = i + d; ptr = f[i][j];
            for(int k = i; k < j; k++)
            {
                f1 = f[i][k], f2 = f[k + 1][j];
                for(int r = 1; r <= H; r++)
                    for(int c = 1; c <= W; c++)
                        down(ptr[r][c], f1[r][c] + f2[r][c]);
            }
            tcnt = 0;
            for(int r = 1; r <= H; r++)
                for(int c = 1; c <= W; c++)
                    if(ptr[r][c] < INF) fy[tcnt++] = pii(r,c);
            sort(fy, fy + tcnt, cmp);  bfs(ptr, tcnt);
        }
    for(int r = 1; r <= H; r++)
        for(int c = 1; c <= W; c++) down(ans, f[1][n][r][c]);
    cout << (ans < INF ? ans : -1) << '\n';
    return 0;
}

参考文献

[1] https://yhx-12243.github.io/OI-transit/records/lydsy3205%3Blg3638%3Buoj107.html


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录