图论杂记 2025.8.5始

Re:从零开始的图论生活

0.1 洛谷P3638 [APIO2013] 机器人

题面太长,不放

考虑到每个机器人最终停下来的位置是固定的,可以记忆化搜索把终点存下来。

具体的,代码中 \(dp_{i,j,k}\) 表示在 \((i,j)\),朝 \(k\) 方向的终点。

因为合并的机器人是连续的,可以使用一种类似于区间 \(DP\) 的东西来对最优答案进行转移。

(有大佬说像斯坦纳树,但我不会

所以建立分层图之后,\(dis_{i,j,k}\) 表示 \(i \to j\) 间的机器人走到 \(k\) 点的最小距离,这里 \(k\) 表示能走地方的编号(见代码 \(recpos\) 数组)。

\(\huge \mathscr{Code}\)

#include<bits/stdc++.h>
using namespace std;
const int N = 505, INF = 1e9;
int dx[4] = { -1,0,1,0 }, dy[4] = { 0,1,0,-1 };
int n, w, h, k, mp[N][N], dp[N][N][4], vis[N][N][4];
int recpos[N][N], tot;
char ch;
vector<int> g[N * N * 4];
int Memdfs(int x, int y, int dir) {
    if (vis[x][y][dir]) return dp[x][y][dir] = -INF; // loop
    if (dp[x][y][dir] != -1) return dp[x][y][dir];
    vis[x][y][dir] = 1;
    int kx = x + dx[dir], ky = y + dy[dir];
    if (mp[kx][ky] == 0) dp[x][y][dir] = recpos[x][y];
    else if (mp[kx][ky] == 11) dp[x][y][dir] = Memdfs(kx, ky, (dir + 3) % 4);
    else if (mp[kx][ky] == 12) dp[x][y][dir] = Memdfs(kx, ky, (dir + 1) % 4);
    else dp[x][y][dir] = Memdfs(kx, ky, dir);
    vis[x][y][dir] = 0;
    return dp[x][y][dir];
}
int dis[10][10][N * N], q1[N * N], q2[N * N];
bool inq[N * N];
void SPFA(int* s) {
    int cnt = 0;
    for (int i = 1; i <= tot; i++) {
        if (s[i] < 0x3f3f3f3f) {
            q1[++cnt] = i;
            inq[i] = true;
        }
    }
    sort(q1 + 1, q1 + cnt + 1, [&](int a, int b) { return s[a] < s[b]; });
    int cur = 1, hd = 1, tl = 0;
    while (cur <= cnt || hd <= tl) {
        int t = INF;
        if (cur <= cnt && (hd > tl || s[q1[cur]] <= s[q2[hd]])) t = q1[cur], cur++;
        else t = q2[hd], hd++;
        inq[t] = false;
        for (int e : g[t]) {
            if (s[e] > s[t] + 1) {
                s[e] = s[t] + 1;
                if (!inq[e]) inq[e] = true, q2[++tl] = e;
            }
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> k >> w >> h;
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            cin >> ch;
            int c;
            if (ch >= '1' && ch <= '9') c = ch - '0';
            else if (ch == 'A') c = 11;
            else if (ch == 'C') c = 12;
            else if (ch == '.') c = 10;
            else c = 0;
            if (c) recpos[i][j] = ++tot;
            mp[i][j] = c;
        }
    }
    memset(dp, -1, sizeof(dp));
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            if (mp[i][j]) {
                Memdfs(i, j, 0);
                Memdfs(i, j, 1);
                Memdfs(i, j, 2);
                Memdfs(i, j, 3);
            }
        }
    }
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            if (mp[i][j]) {
                for (int d = 0; d < 4; d++) {
                    if (dp[i][j][d] != -INF && dp[i][j][d] != recpos[i][j]) {
                        g[recpos[i][j]].push_back(dp[i][j][d]);
                    }
                }
            }
        }
    }
    memset(dis, 0x3f, sizeof(dis));
    for (int i = 1; i <= h; i++) {
        for (int j = 1; j <= w; j++) {
            if (mp[i][j] >= 1 && mp[i][j] <= 9) {
                dis[mp[i][j]][mp[i][j]][recpos[i][j]] = 0;
            }
        }
    }
    for (int len = 1; len <= k; len++) {
        for (int l = 1, r = l + len - 1; r <= k; l++, r++) {
            for (int mid = l; mid < r; mid++) {
                for (int i = 1; i <= tot; i++) {
                    dis[l][r][i] = min(dis[l][r][i], dis[l][mid][i] + dis[mid + 1][r][i]);
                }
            }
            SPFA(dis[l][r]);
        }
    }
    int ans = INF;
    for (int i = 1; i <= tot; i++) ans = min(ans, dis[1][k][i]);
    cout << (ans == INF ? -1 : ans);
    return 0;
}
posted @ 2025-08-05 10:49  OrangeRED  阅读(8)  评论(0)    收藏  举报