[APIO2013]机器人
description
solution
dpl,r,i,j:dp_{l,r,i,j}:dpl,r,i,j: 在(i,j)(i,j)(i,j)位置合并编号为[l,r][l,r][l,r]区间内的所有机器人的最小花费
toi,j,k:to_{i,j,k}:toi,j,k: 从(i,j)(i,j)(i,j)以kkk方向推机器人最终停止的位置,用dfs
记忆化搜索模拟即可
dpl,r,i,j=min{dpl,k,i,j+dpk+1,r,i,j∣l≤i<r}dp_{l,r,i,j}=\min\{dp_{l,k,i,j}+dp_{k+1,r,i,j}\big| l\le i<r\}dpl,r,i,j=min{dpl,k,i,j+dpk+1,r,i,j∣∣l≤i<r}
dpl,r,i,j+1→toi,j,kdp_{l,r,i,j}+1\rightarrow to_{i,j,k}dpl,r,i,j+1→toi,j,k 该转移就是一层在(i,j)(i,j)(i,j)位置上的最短路,用类bfs
的spfa
跑即可
code
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define LEN 500000
struct node {int x, y;node(){}node( int X, int Y ) {x = X, y = Y;}
}robot[10], to[505][505][4], s[500005], t[500005];
int n, col, row, cnt, top;
char room[505][505];
int dx[4] = { 1, 0, -1, 0 }, dy[4] = { 0, -1, 0, 1 };
int f[10][10][505][505];
int id[505][505][4];
bool vis[505][505];
int w[1000005], sum[1000005];node dfs( int x, int y, int k ) {if( id[x][y][k] == cnt ) return node( 0, -1 );id[x][y][k] = cnt;if( to[x][y][k].y ) return to[x][y][k];int dir = k;if( room[x][y] == 'C' ) dir = ( dir + 1 ) % 4;if( room[x][y] == 'A' ) dir = ( dir + 3 ) % 4;int tx = x + dx[dir], ty = y + dy[dir];if( tx < 1 || tx > row || ty < 1 || ty > col || room[tx][ty] == 'x' )return to[x][y][k] = node( x, y );elsereturn to[x][y][k] = dfs( tx, ty, dir );
}void spfa( int l, int r ) {memset( sum, 0, sizeof( sum ) );memset( vis, 0, sizeof( vis ) );int minn = inf, maxx = -inf;for( int i = 1;i <= top;i ++ ) {sum[w[i]] ++;minn = min( minn, w[i] );maxx = max( maxx, w[i] );vis[s[i].x][s[i].y] = 1;}//类后缀数组方法对队列中当前层位置进行手动排序编号 for( int i = minn + 1;i <= maxx;i ++ ) sum[i] += sum[i - 1];for( int i = top;i;i -- )t[sum[w[i]] --] = s[i];for( int i = 1;i <= top;i ++ )s[i] = t[top - i + 1];int head = 0, tail = 0, now; node u, v;//%LEN:手动进行空间回收利用 while( top || head != tail ) {now = head % LEN + 1;if( head == tail || ( top && f[l][r][s[top].x][s[top].y] < f[l][r][t[now].x][t[now].y] ) )u = s[top], top --;elsehead = now, u = t[head];vis[u.x][u.y] = 0;for( int i = 0;i < 4;i ++ ) {v = to[u.x][u.y][i]; if( v.x && f[l][r][v.x][v.y] > f[l][r][u.x][u.y] + 1 ) {f[l][r][v.x][v.y] = f[l][r][u.x][u.y] + 1;if( ! vis[v.x][v.y] ) {vis[v.x][v.y] = 1;tail = tail % LEN + 1;t[tail] = v;}}}}
}int main() {memset( f, 0x3f, sizeof( f ) );scanf( "%d %d %d", &n, &col, &row );for( int i = 1;i <= row;i ++ ) {scanf( "%s", room[i] + 1 );for( int j = 1;j <= col;j ++ )if( room[i][j] < '0' || room[i][j] > '9' )continue;else {int k = room[i][j] - '0';robot[k] = node( i, j );f[k][k][i][j] = 0;}}//to(i,j,k):(i,j)位置按k方向推机器人最终停止的位置 for( int i = 1;i <= row;i ++ )for( int j = 1;j <= col;j ++ )if( room[i][j] == 'x' )continue;elsefor( int k = 0;k < 4;k ++ )cnt ++, to[i][j][k] = dfs( i, j, k );for( int i = 1;i <= n;i ++ )s[top = 1] = robot[i], w[1] = 0, spfa( i, i );//f(l,r,i,j,):在(i,j)位置上合并[l,r]编号内的机器人的最小花费 for( int len = 1;len <= n;len ++ )//区间DP 由小到大 for( int l = 1;l <= n;l ++ ) {int r = l + len;if( r > n ) break;for( int i = 1;i <= row;i ++ )for( int j = 1;j <= col;j ++ )for( int k = l;k < r;k ++ )f[l][r][i][j] = min( f[l][r][i][j], f[l][k][i][j] + f[k + 1][r][i][j] );top = 0;for( int i = 1;i <= row;i ++ )for( int j = 1;j <= col;j ++ )if( f[l][r][i][j] < inf ) //状态本身要存在才能进行下一层转移 s[++ top] = node( i, j ), w[top] = f[l][r][i][j];spfa( l, r );}int ans = inf;for( int i = 1;i <= row;i ++ )for( int j = 1;j <= col;j ++ )ans = min( ans, f[1][n][i][j] );printf( "%d\n", ans < inf ? ans : -1 );return 0;
}