3205: [Apio2013]机器人

3205: [Apio2013]机器人

Time Limit: 15 Sec   Memory Limit: 128 MB
Submit: 525   Solved: 131
[ Submit][ Status][ Discuss]

Description

VRI(Voltron机器人学会)的工程师建造了 n个机器人。任意两个兼容的机器人站在同一个格子时可以合并为一个复合机器人。我们把机器人用 1至 n编号(n ≤ 9)。如果两个机器人的编号是连续的,那么它们是兼容的,可以合并成一个复合机器人。最初这   n   个机器人各自都只有唯一的编号。而一个由两个或以上的机器人合并构成的复合机器人拥有两个编号,分别是构成它的所有机器人中最小和最大的编号。例如, 2号机器人只可以与 1号或 3号机器人合并。若 2号机器人与 3号机器人合并,可构成编号为 2-3的复合机器人。如果编号为 2-3的复合机器人与编号为 4-6的复合机器人合并,可构成编号为 2-6的复合机器人。当所有机器人合并以后则构成 1-n复合机器人。工程师把这 n个机器人放在了一个封闭的房间中,房间四周均是墙。该房间被划分成 w     h    个方格。有些方格有障碍物,机器人不可经过或停留;其余方格允许多个机器人停留,同时允许机器人经过。任何时候一个机器人只占用一个方格。初始时刻,所有机器人均在不同的方格中。这些原始的机器人不会自发地移动。它们只有被工程师沿   x轴或 y轴推动后,才会沿推动的方向不断向前直线移动,直至碰到障碍物或墙停止移动。停止移动后,它会扫描当前的格子是否存在可以与它合并的机器人,如果有,则合并并继续检查,直至不能再合并为止。工程师只能沿水平向左、水平向右、竖直向上、竖直向下四个方向推动机器人,并且,在机器人尚未停止移动时,不允许推动其它机器人,因此任何时刻,房间中都只能有一个机器人移动,为了帮助机器人转向,工程师在一些格子中放置了转向器。具体地说,转向器分为顺时针转向器(右转器)和逆时针转向器(左转器),顺时针转向器可以使到达该格子的机器人沿顺时针方向转向   90_;逆时针转向器可以使到达该格子的机器人沿逆时针方向转向 90_。现在,我们将告诉你初始时刻房间内的信息。请你计算工程师最少共计需要推动机器人多少次,才能把所有的 n个机器人全部合并(如果可能的话)。

Input

你的程序必须从标准输入读入。输入的第 1行包含 3个整数 n、w和 h,用空格隔开。输入文件中接下来的 h行描述初始时刻房间内的信息,每行包含w个字符。这w* h 字符中每一个表示房间中的一个格子,意义如下:
 
‘ 1’至‘9’:表示该方格中有一个机器人,编号为这个数字;
‘ x’:表示该方格有障碍物;
 
‘ A’:表示该方格中有一个逆时针转向器;
 
‘ C’:表示该方格中有一个顺时针转向器;
‘ .’:表示该方格为空地。

Output

你的程序必须输出到标准输出。输出仅一个整数,表示最少需要推动的次数。
若不能使所有机器人全部合并,输出-1。

Sample Input

4 10 5
1.........
AA...x4...
..A..x....
2....x....
..C.3.A...



Sample Output

5

HINT



第一步:向右推动 3 号机器人,当它碰到转向器后会向上继续移动,直至碰到墙壁停止移动。第二步:向上推动 4 号机器人,当它碰到墙壁后停止移动,与3 号机器人合并,构成  3-4 号机器人 第三步:向上推动 2 号机器人,当它碰到转向器后会向左移动,由于左侧为墙壁,故停留在原地。第四步:向右推动  2 号机器人,由于它在一个转向器上,故它会向上移动,直至碰到墙壁停止移动,与  1 号机器人合并,构成 1-2 号机器人。第五步:向左推动  3-4 号机器人,当它碰到墙壁后停止移动,与 1-2 号机器人合并,构成  1-4 号机器人。 

≤ 9≤ 500    ≤ 500

Source

[ Submit][ Status][ Discuss]

斯坦纳树
f[l][r][i][j]:已经将[l,r]的机器人合并,以(i,j)为根,最少步数
方程显然

注意一些优化:
1.图中有环需要特判
2.裸的SPFA会TLE

对于边权全部为1,有多个源点的SPFA可以采用如下形式降低复杂度为线性
先将所有源点按照初始值排序,放入队列1
每次取出队列1、队列2的队头,用少的去松弛,新点如队列2

f[l][r][i][j]寻址速度快于f[i][j][l][r]

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
struct Point{
    int x,y;
    Point(){}
    Point(int x,int y): x(x),y(y){}
};
 
const int maxn = 501;
const int dx[4] = {1,0,-1,0};
const int dy[4] = {0,1,0,-1};
const int INF = 1E9;
 
int n,m,t,cnt,tot,tail,f[10][10][maxn][maxn],Nex[maxn][maxn][4]
    ,L,R,px[11],py[11],vis[maxn][maxn],Ney[maxn][maxn][4];
char p[maxn][maxn];
bool Vis[maxn][maxn][4],inq[maxn][maxn];
Point Able[maxn*maxn],Now[maxn*maxn];
 
queue <Point> Q1,Q2;
 
void Dfs(int x,int y,int l)
{
    if (Vis[x][y][l]) {
        if (!Nex[x][y][l])
            Nex[x][y][l] = Ney[x][y][l] = -INF;
        return;
    }
    Vis[x][y][l] = cnt;
    int kl = l;
    if (p[x][y] == 'A')
        kl = (kl + 1) % 4;
    if (p[x][y] == 'C')
        kl = (kl + 3) % 4;
    int xx = x + dx[kl];
    int yy = y + dy[kl];
    if (xx < 1 || xx > n || yy < 1 || yy > m || p[xx][yy] == 'x') {
        Nex[x][y][l] = x;
        Ney[x][y][l] = y;
        return;
    }
    Dfs(xx,yy,kl);
    Nex[x][y][l] = Nex[xx][yy][kl];
    Ney[x][y][l] = Ney[xx][yy][kl];
}
 
bool cmp(const Point &A,const Point &B)
{
    return f[L][R][A.x][A.y] < f[L][R][B.x][B.y];
}
 
void SPFA(int l,int r)
{
    L = l; R = r;
    sort(Now + 1,Now + tail + 1,cmp);
    for (int i = 1; i <= tail; i++)
        Q1.push(Now[i]);
    while (!Q1.empty() || !Q2.empty()) {
        Point k;
        if (Q1.empty())
            k = Q2.front(),Q2.pop();
        else if (Q2.empty())
            k = Q1.front(),Q1.pop();
        else {
            Point k1 = Q1.front();
            Point k2 = Q2.front();
            if (f[l][r][k1.x][k1.y] < f[l][r][k2.x][k2.y]) 
                k = k1,Q1.pop();
            else k = k2,Q2.pop();
        }
        vis[k.x][k.y] = 0;
        for (int Next = 0; Next < 4; Next++) {
            int xx = Nex[k.x][k.y][Next];
            int yy = Ney[k.x][k.y][Next];
            if (xx == -INF) continue;
            if (f[l][r][xx][yy] > f[l][r][k.x][k.y] + 1) {
                f[l][r][xx][yy] = f[l][r][k.x][k.y] + 1;
                if (vis[xx][yy] != cnt) 
                    vis[xx][yy] = cnt,Q2.push(Point(xx,yy));
                if (!inq[xx][yy]) {
                    inq[xx][yy] = 1;
                    Able[++tot] = Point(xx,yy);
                }
            }
        }
    }
}
 
int main()
{
    freopen("3205.in","r",stdin);
    freopen("3205.out","w",stdout);
     
    cin >> t >> m >> n;
    for (int i = 1; i <= n; i++)
        scanf("%s",p[i] + 1);
    ++cnt;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            for (int l = 0; l < 4; l++)
                if (!Vis[i][j][l])
                    Dfs(i,j,l);
            if ('1' <= p[i][j] && p[i][j] <= '9') {
                px[p[i][j] - '0'] = i;
                py[p[i][j] - '0'] = j;
            }
        }
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            for (int l = 1; l <= t; l++)
                for (int r = l; r <= t; r++)
                    f[l][r][i][j] = INF;
    for (int i = 1; i <= t; i++) {
        f[i][i][px[i]][py[i]] = 0;
        Able[++tot] = Point(px[i],py[i]);
        inq[px[i]][py[i]] = 1;
    }
    for (int k = 0; k < t; k++)
        for (int l = 1; l <= t; l++) {
            int r = l + k;
            if (r > t) break;
            ++cnt; tail = 0;
            for (int i = 1; i <= tot; i++) {
                int x = Able[i].x;
                int y = Able[i].y;
                for (int K = l; K < r; K++)
                    f[l][r][x][y] = min(f[l][r][x][y],f[l][K][x][y] + f[K+1][r][x][y]);
                if (f[l][r][x][y] < INF)
                    Now[++tail] = Point(x,y),vis[x][y] = cnt; 
            }
            SPFA(l,r);
        }
    int Ans = INF;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            Ans = min(Ans,f[1][t][i][j]);
    if (Ans == INF) cout << -1;
    else cout << Ans;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值