roiti46's blog

主に競プロ問題の解説を載せてます

TopCoder SRM 659 Div2 Med: PublicTransit

制限が5secな珍しい問題。ネスト深すぎなので分離すればよかったかな。

問題はこちら

問題

R×Cの2次元セルがある。あるセルからは上下左右のセルにコスト1で移動できるとする。またテレポーターを1組置くことができ、片方のテレポーターからもう一方へコスト0で移動できる。テレポーターのあるマスに移動したときにはテレポーターを使うかそのままそのセルにとどまるか選択できる。任意のセル間の移動コストの最大値を最小化するようにテレポーターを設置したときの、最小化された移動コストの最大値を答えよ。

解法

2点間距離はワーシャルフロイドで求め、テレポーターの設置場所は重複の無い全探索をすればいい。
オーダーはO((RC)5)になるがR,Cが最大10で制限が5secなのでこれでも間に合う。
R = C = 1のコーナーケースに注意するのを忘れない。

const int inf = 1 << 28;
int dxy[] = {1, 0, -1, 0, 1};
class PublicTransit {
public:
    int minimumLongestDistance(int R, int C) {
        if (R == 1 && C == 1) return 0;
        int G[101][101];
        
        for (int y = 0; y < 101; y++)
            for (int x = 0; x < 101; x++) G[y][x] = inf;
            
        for (int y = 0; y < R; y++) {
            for (int x = 0; x < C; x++) {
                for (int i = 0; i < 4; i++) {
                    int nx = x + dxy[i], ny = y + dxy[i + 1];
                    if (0 <= nx && nx < C && 0 <= ny && ny < R)
                        G[x + C * y][nx + C * ny] = G[nx + C * ny][x + C * y] = 1;
                }
            }
        }
        
        int ans = inf;
        for (int y = 0; y < R / 2 + 1; y++) {
            for (int x = 0; x < C / 2 + 1; x++) {
                for (int cy = R / 2; cy < R; cy ++) {
                    for (int cx = C / 2; cx < C; cx++) {
                        if (x == cx && y == cy) continue;
                        int tmp = 0;
                        int g[101][101];
                        for (int r = 0; r < 101; r++)
                            for (int c = 0; c < 101; c++) g[r][c] = G[r][c];

                        g[x + C * y][cx + C * cy] = g[cx + C * cy][x + C * y] = 0;

                        for (int k = 0; k < R * C; k++) 
                            for (int i = 0; i < R * C; i++) 
                                for (int j = 0; j < R * C; j++)
                                    g[i][j] = min(g[i][j], g[i][k] + g[k][j]);

                        for (int i = 0; i < R * C; i++)
                            for (int j = i + 1; j < R * C; j++)
                                if (g[i][j] < inf) tmp = max(tmp, g[i][j]);
                                
                        ans = min(ans, tmp);
                    }
                }
            }
        }
        return ans;
    }
};

まとめ

Div2Medにしては難易度高い方な気がする