Facebook Hacker Cup: после танцевальной битвы - PullRequest
6 голосов
/ 15 января 2011

Мы хотим найти кратчайший путь между двумя точками в специальной сетке.Мы можем перемещаться между соседними квадратами за один ход, но мы также можем перемещаться между ячейками одного типа (существует 10 типов) за один ход независимо от расстояния между ними.

Как мы можем найтиколичество шагов, необходимых для перемещения между двумя точками для сетки размером до 100x100?

Ответы [ 6 ]

3 голосов
/ 15 января 2011

Я решил эту проблему во время конкурса, используя BFS.

Проблема может быть смоделирована в виде графика.Каждая ячейка является узлом и имеет ребро с каждой соседней ячейкой.Вместо явного построения графика, мы можем просто сохранить неявный график, посещая каждую ячейку и ее соседей, вычисляя координаты сетки по мере необходимости.

Каждая ячейка также имеет ребро для всех ячеек одного цвета.Мы можем добавить это к нашему графику, сохранив списки ячеек каждого цвета и посетив все ячейки того же цвета, что и текущая ячейка.

Будет работать что-то вроде Dijkstra или A * (по сути, это BFS сочередь приоритетов / эвристика в конце концов), но реализация этого для такой простой проблемы будет серьезным излишним.

Код следует (на C ++):

#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#include <map> 

using namespace std;

char grid[101][101];
int cost[101][101];

vector<pair<int,int> > colour[10]; // lists of same coloured cells

//used to compute adjacent cells
int dr[]={ 0, 0, 1,-1};
int dc[]={ 1,-1, 0,0};

int rows,cols; // total rows and coloumns


int bfs(pair<int,int> start){
    memset(cost,-1,sizeof(cost)); // all unvisited nodes have negative cost to mark them
    cost[start.first][start.second]=0; // start node has cost 0

    queue<pair<int,int> > Q;
    Q.push(start);

    while (!Q.empty()){

        pair<int,int> node=Q.front();Q.pop();
        int r=node.first;
        int c=node.second;
        int cst=cost[r][c];
        if (grid[r][c]=='E'){
            return cst;
        }

        // search adjacent cells
        for(int i=0;i<4;i++){
            int nr=r+dr[i];
            int nc=c+dc[i];
            if (nr>=0 && nr<rows && nc>=0 && nc<cols && cost[nr][nc]<0 && grid[nr][nc]!='W'){
                cost[nr][nc]=cst+1;
                Q.push(make_pair(nr,nc));
            }
        }

        // search cells of the same colour
        if (grid[r][c]>='1' && grid[r][c]<='9'){
            vector<pair<int,int> > &trans=colour[grid[r][c]-'0'];
            for(int i=0;i<trans.size();i++){
                pair<int,int> next=trans[i];
                if (cost[next.first][next.second]<0){
                    cost[next.first][next.second]=cst+1;
                    Q.push(next);
                }
            }
        }
    }
    return -1;
}

int main(){
    int N;
    cin>>N;
    while(N--){
        cerr<<"cases left"<<N<<endl;
        cin>>rows>>cols;
        pair<int,int> start;
        for(int i=0;i<10;i++) colour[i].clear();

        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                cin>>grid[i][j];

                if (grid[i][j]=='S'){
                    start=make_pair(i,j);
                }
                else if (grid[i][j]>='1' && grid[i][j]<='9'){
                    colour[grid[i][j]-'0'].push_back(make_pair(i,j));
                }
            }
        }
        cout<<bfs(start)<<"\n";
    }

    return 0;
}
2 голосов
/ 15 января 2011

Составьте 10 массивов, каждый из которых содержит ячейки соответствующего типа.Теперь запустите Dijkstra (или BFS), но при посещении ячейки типа i добавьте все ячейки типа i в очередь и очистите соответствующий массив.Таким образом, вам не нужно посещать каждое ребро между ячейками одного типа, и сложность O (n ^ 2) вместо O (n ^ 4)

1 голос
/ 15 января 2011

Вы можете представить лабиринт в виде графика и решить его с помощью BFS (он работает для этого случая и проще, чем Дейкстра и A *).

Чтобы заполнить края, вы можете сделать это:

for each row
   for each line
      if char == 'S' mark this point as start
      if char == 'E' mark this point as end
      if char != 'W' then
         create edges between this point and its adjacents (only if they exist and aren't a wall)
         if char >= '1' and char <= '9'
            create edges between this point and everyone with the same color

Затем примените BFS ( Начало в ширину ) от start до end и все готово.

PS: Чтобы сэкономить память, вы должны представить график с помощью списков смежности, поскольку у большинства узлов будет 4 соседа.

1 голос
/ 15 января 2011

Я думаю, что самый простой способ решить это моделировать сетку в виде графика. Создайте ребро между двумя соседями, а также создайте одно ребро между двумя узлами одного типа. После этого простая BFS от источника до места назначения может ответить по кратчайшему пути между двумя узлами.

http://en.wikipedia.org/wiki/Breadth-first_search

Сложность O (V + E). V - количество узлов, 100x100, E - количество ребер

.

Люди здесь упоминают Дейкстру. Это решает, но это необходимо, так как все края стоят один. Дейкстра является частным случаем алгоритма A-star, поэтому A * также слишком много для этой проблемы.

Будьте проще, BFS!

Это O (R * C) время и O (R * C) пространственное решение:

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

public class DanceBattle {
    static final int INF = (int)(Integer.MAX_VALUE * 0.5);
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int N = in.nextInt();
        for(int i = 0; i < N ; ++i) {
            int R = in.nextInt();
            int C = in.nextInt();
            int V = R*C;
            int node = 0, start = 0, end = 0;
            int nodeColor[] = new int[V];
            List<Integer>[] colorMap = new List[10];
            for(int color=0;color<10;++color)
                colorMap[color] = new LinkedList<Integer>();
            for(int r = 0; r < R; ++r) {
                String row = in.next();
                for(int c = 0; c<row.length(); ++c) {
                    char v = row.charAt(c);
                    if(v == 'S' ) start = node;
                    else if (v == 'E') end = node;
                    else if (v == 'W') nodeColor[node] = -1;
                    else {
                        int color = (int)(v - '0');
                        nodeColor[node] = color;
                        colorMap[color].add(node);
                    }
                    node++;
                }
            }
            int neighborhood4[][] = new int[V][4];
            for(int j =0 ; j< V; ++j) {
                int c = j % C;              
                int r = (j - c)/C;
                int grad = 0;
                if(neighbors(r,c,r,c+1, R, C))
                    neighborhood4[j][grad++] = (r) * C + (c+1);
                if(neighbors(r,c,r,c-1, R, C))
                    neighborhood4[j][grad++] = (r) * C + (c-1);
                if(neighbors(r,c,r+1,c, R, C))
                    neighborhood4[j][grad++] = (r+1) * C + (c);
                if(neighbors(r,c,r-1,c, R, C))
                    neighborhood4[j][grad++] = (r-1) * C + (c);
                if(grad < 4)
                    neighborhood4[j][grad] = -1;
            }

            //bfs
            int qbegin, qend;
            int[] Q = new int[V];
            int[] dist = new int[V];
            for(int j=0; j<V; j++) dist[j] = INF;
            dist[start] = 0;
            qbegin = qend = 0;
            Q[qend++] = start;
            int complexity = 0;
            while(qbegin != qend) {
                int currNode = Q[qbegin++];
                for(int j=0; j<4; j++) {
                    int neighbor = neighborhood4[currNode][j];
                    if(neighbor == -1) break;

                    int color = nodeColor[neighbor];
                    if(dist[neighbor] == INF && color != -1 ) {
                        Q[qend++] = neighbor;
                        dist[neighbor] = dist[currNode] + 1;
                    }
                    complexity++;                       
                }
                int color = nodeColor[currNode];
                if (color == 0)
                    continue;
                Iterator<Integer> iter = colorMap[color].iterator();
                while(iter.hasNext()) {
                    int viz = iter.next();
                    if(dist[viz] == INF) {
                        Q[qend++] = viz;
                        dist[viz] = dist[currNode] + 1;
                    }
                    iter.remove();
                    complexity++;                       
                }
            }
            System.out.printf("%d\n",dist[end]);
            //System.out.printf("(compl. %d V=%d constant: %.2f)\n", complexity, V, ((float)complexity) /  V);
        }
    }

    private static boolean neighbors(int ar, int ac, int br, int bc, int R, int C) {
        if(ar < 0 || ac < 0 || br < 0 || bc < 0) return false;
        if(ar >= R || ac >= C || br >= R || bc >= C) return false;
        return (Math.abs(ar - br) <= 1 && Math.abs(ac - bc) ==0) || (Math.abs(ar - br) == 0 && Math.abs(ac - bc) <=1);
    }
}
0 голосов
/ 15 января 2011

Это можно сделать с помощью Динамическое программирование .Здесь вы определяете связь и начинаете с первого квадрата.Вы ведете учет минимального расстояния, найденного для перемещения к определенной точке.Когда вы достигли своей конечной точки, вы найдете путь, возвращая маршрут самого быстрого спуска.

В вашем случае вы должны определить связи между соседними ячейками, а также между ячейками одного цвета.

0 голосов
/ 15 января 2011

Постройте график всех квадратов в виде вершин с ребрами, отмечающими возможные ходы, а затем дайте алгоритму Дейкстры решить это.

...