【UOJ #107】【APIO 2013】ROBOTS

http://uoj.ac/problem/107
\(f(l,r,i,j)\)表示\([l,r]\)中的机器人聚集到\((i,j)\)需要花的最小操作数。
\(f(l,r,i,j)=\min\left\{\begin{array}{rcl} f(l,k,i,j)+f(k+1,r,i,j)\\ f(l,r,i',j'),(i',j')\rightarrow(i,j)\end{array}\right.\)
斯坦纳树的模型,对于每个\([l,r]\)的状态,处理完第一行后再跑一遍双队列广搜处理第二行。
用基数排序优化,时间复杂度\(O(n^3+n^2hw)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 503;
const int dx[4] = {-1, 0, 1, 0};
const int dy[4] = {0, -1, 0, 1};

struct data {
    int x, y;
    data (int _x = 0, int _y = 0) : x(_x), y(_y) {}
} to[N][N][4];

bool vis[N][N][4];
char s[N][N];
int f[10][10][N][N], n, w, h;

data work(int x, int y, int tmp) {
    if (vis[x][y][tmp]) return to[x][y][tmp];
    vis[x][y][tmp] = true;
    if (s[x][y] == 'A') (++tmp) &= 3;
    if (s[x][y] == 'C') (--tmp) &= 3;
    if (s[x + dx[tmp]][y + dy[tmp]] == 'x')
        return data(x, y);
    else
        return to[x + dx[tmp]][y + dy[tmp]][tmp] = work(x + dx[tmp], y + dy[tmp], tmp);
}

int tot = 0, l, r, id[N * N];
data q1[N * N], q2[N * N], tt;

template <typename T> void check_min(T &a, T b) {if (b < a) a = b;}
template <typename T> void check_max(T &a, T b) {if (b > a) a = b;}

bool cmp(data A, data B) {return f[l][r][A.x][A.y] < f[l][r][B.x][B.y];}

int c[N * N * 20];

void BFS() {
//  stable_sort(q1 + 1, q1 + tot + 1, cmp);
    int mx = 0, num;
    for (int i = 1; i <= tot; ++i) if ((num = f[l][r][q1[i].x][q1[i].y]) != 1010580540) check_max(mx, num);
    memset(c, 0, sizeof(int) * (mx + 2));
    for (int i = 1; i <= tot; ++i) {
        num = f[l][r][q1[i].x][q1[i].y];
        if (num != 1010580540) ++c[num];
        else ++c[mx + 1];
    }
    for (int i = 1; i <= mx + 1; ++i) c[i] += c[i - 1];
    for (int i = tot; i >= 1; --i) {
        num = f[l][r][q1[i].x][q1[i].y];
        if (num == 1010580540) num = mx + 1;
        id[c[num]--] = i;
    }
    int head = 1, tail = 0, tmp = 1, x, y, x2, y2;
    while (head <= tail || tmp <= tot) {
        x = q2[head].x; y = q2[head].y;
        x2 = q1[id[tmp]].x; y2 = q1[id[tmp]].y;
        if (head <= tail && tmp <= tot && f[l][r][x][y] < f[l][r][x2][y2] || tmp > tot) ++head;
        else ++tmp, x = x2, y = y2;
        for (int d = 0; d < 4; ++d) {
            tt = to[x][y][d];
            if (tt.x == 0) continue;
            if (f[l][r][tt.x][tt.y] > f[l][r][x][y] + 1) {
                f[l][r][tt.x][tt.y] = f[l][r][x][y] + 1;
                q2[++tail] = tt;
            }
        }
    }
}

int main() {
    scanf("%d%d%d", &n, &w, &h);
    
    memset(f, 60, sizeof(f));
    
    for (int i = 1; i <= h; ++i)
        scanf("%s", s[i] + 1);
    for (int i = 1; i <= h; ++i) s[i][0] = s[i][w + 1] = 'x';
    for (int i = 1; i <= w; ++i) s[0][i] = s[h + 1][i] = 'x';
    for (int i = 1 ; i <= h; ++i)
        for (int j = 1; j <= w; ++j)
            if (s[i][j] != 'x')
                for (int dt = 0; dt < 4; ++dt)
                    to[i][j][dt] = work(i, j, dt);
    
    for (int i = 1; i <= h; ++i)
        for (int j = 1; j <= w; ++j) q1[++tot] = data(i, j);
    
    bool flag;
    for (int i = n; i >= 1; --i) {
        flag = false;
        for (int ii = 1; ii <= h; ++ii) {
            for (int jj = 1; jj <= w; ++jj)
                if (s[ii][jj] == '0' + i) {
                    f[i][i][ii][jj] = 0;
                    flag = true;
                    break;
                }
            if (flag) break;
        }
        l = r = i; BFS();
        for (int j = i + 1; j <= n; ++j) {
            for (int k = i; k < j; ++k)
                for (int ii = 1; ii <= h; ++ii)
                    for (int jj = 1; jj <= w; ++jj)
                        check_min(f[i][j][ii][jj], f[i][k][ii][jj] + f[k + 1][j][ii][jj]);
            l = i; r = j; BFS();
        }
    }
    
    int ans = 0x7fffffff;
    for (int i = 1; i <= h; ++i)
        for (int j = 1; j <= w; ++j)
            check_min(ans, f[1][n][i][j]);
    printf("%d\n", ans == 1010580540 ? -1 : ans);
    return 0;
}

转载于:https://www.cnblogs.com/abclzr/p/6706229.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值