Почему эта реализация Дейкстры (графа) не работает?

Я сделал следующую реализацию для этой проблемы: http://www.spoj.pl/problems/SHOP/ < / а>


#include<iostream>
#include<stdio.h>
#include<queue>
#include<conio.h>
#include<string.h>
using namespace std;

struct node
{
    int x;
    int y;
    int time;
};
bool operator <(const node &s,const node &r)
{
    if(s.time>r.time)
    return true;
    else return false;
}
node beg,src,dest,tempa;
int b,a,temp;
int map[25][25];
bool vis[25][25];
int X[]={1,0,-1,0};
int Y[]={0,1,0,-1};


int djs_bfs(node src,node dest)
{
    int result=0;
    priority_queue<node>pq;
    pq.push(src);
    while(!pq.empty())
    {
        node top = pq.top();
        pq.pop();
        if(top.x==dest.x && top.y==dest.y) return result;
        if(top.x<0 || top.x>=a) continue;
        if(top.y<0 || top.y>=b) continue;
        if(vis[top.x][top.y]) continue;

        vis[top.x][top.y]=true;
        result+=map[top.x][top.y];
        for(int i=0;i<4;i++)
        {
            tempa.x=top.x+X[0];
            tempa.y=top.y+Y[0];
            tempa.time=map[top.x+X[0]][top.y+Y[0]];
            pq.push(tempa);
        }
    }
    return -1;
}

int main()
{
    memset(vis,false,sizeof(vis));
    scanf("%d %d",&a,&b);
    while(a != 0)
    {
        for(int i=0;i<a;i++)
            for(int j=0;j<b;j++)
            {
                scanf("%c",&temp);
                if(temp=='X') {map[i][j]=0;vis[i][j]=true;}
                if(temp=='S') {src.x=i;src.y=j;src.time=0;}
                if(temp=='D') {dest.x=i;dest.y=j;dest.time=0;}
                else map[i][j]=temp-'0';
            }
        cout<<djs_bfs(src,dest)<<endl;
        scanf("%d %d",&a,&b);
    }
    return 0;
    getch();
}

Я не знаю, почему мой код не дает правильного ответа для тестов. Если кто-то может помочь мне улучшить код, сделайте это: D


person magiix    schedule 03.02.2010    source источник
comment
Вы имеете в виду, что это не работает с образцом ввода? Или он не дает правильных ответов на реальные контрольные примеры?   -  person Gant    schedule 03.02.2010


Ответы (3)


Во-первых, неправильный код разбора графа. Первая строка определяет ширину и высоту, где ширина - это количество символов в строке, а высота - это количество строк. Следовательно, поменяйте местами &a и &b в первом сканировании или поменяйте местами вложенные for циклы (но не оба сразу). Кроме того, мне пришлось добавить фиктивные scanf("%c", &dummy); вызовы в разных местах, чтобы отфильтровать новые строки. Простой дамп, такой как этот, поможет определить, правильно ли была проанализирована ваша карта:

cout << "a=" << a << endl;
cout << "b=" << b << endl;
for (int i=0; i<a; i++) {
    for(int j=0; j<b; j++) {
        cout << (char)('0' + map[i][j]) << ",";
    }
    cout << endl;
}

Примечание: я также установил map[i][j] в 0 для 'S' и 'D', также заменив повторяющиеся операторы if на цепочку if; else if; else. Это делает алгоритм более надежным, поскольку вы можете обычно добавлять время из источника или пункта назначения.

Теперь перейдем к самому алгоритму ....

Каждый цикл алгоритма увеличивает result на вес текущего местоположения карты. Однако алгоритм выполняет поиск по нескольким путям одновременно (т. Е. По количеству записей в очереди с приоритетом), и поэтому result оказывается суммой всех обработанных весов узлов, а не текущим весом пути. Текущий вес пути равен top.temp, поэтому вы можете исключить переменную result и просто вернуть top.temp, когда достигнете места назначения.

Кроме того, как отмечалось в других ответах, вам нужно использовать X[i] и Y[i] во внутреннем цикле, иначе вы будете искать только в одном направлении.

Теперь, из-за сложения / вычитания из X[i] и Y[i], вы, вероятно, получите доступ к map[][] вне диапазона (-1 или 25). Поэтому я рекомендую переместить if охранников во внутренний for цикл для защиты от доступа за пределами допустимого диапазона. Это также позволяет избежать заполнения очереди приоритетов недопустимыми возможностями.

Вот моя версия алгоритма с минимальными исправлениями для справки:

priority_queue<node>pq;
pq.push(src);
while(!pq.empty())
{
    node top = pq.top();
    pq.pop();

    if(top.x==dest.x && top.y==dest.y) return top.time;
    if(vis[top.x][top.y]) continue;

    vis[top.x][top.y]=true;

    for(int i=0;i<4;i++)
    {
        tempa.x=top.x+X[i];
        tempa.y=top.y+Y[i];
        if(tempa.x<0 || tempa.x>=a) continue;
        if(tempa.y<0 || tempa.y>=b) continue;
        tempa.time=top.time + map[tempa.x][tempa.y];
        pq.push(tempa);
    }
}
return -1;

Надеюсь, это поможет.

person Jason Govig    schedule 03.02.2010
comment
Спасибо, постараюсь применить ваши идеи и надеюсь, что это сработает: D - person magiix; 04.02.2010
comment
Я пытался обновить код, но все равно не могу заставить его работать, это ссылка, вы можете проверить ?? Ссылка: codeviewer.org/view/code:bc9 Он даже не генерирует то, что хранится ввод правильно: S - person magiix; 05.02.2010
comment
Вы все еще читаете символы новой строки как часть своих данных. (См. Мой комментарий в ответе о добавлении фиктивных вызовов scanf.) Чтобы исправить это, добавьте вызовы getch() после каждого вызова scanf, который читается в a и b, а также после каждой итерации внешнего цикла for (после внутреннего цикла for). Это означает, что вам понадобятся фигурные скобки для внешнего цикла for, что в любом случае является хорошей практикой. Вы также можете проверить его на наличие ошибок, заявив, что прочитанный символ является новой строкой. - person Jason Govig; 05.02.2010
comment
Что ж, после того, как я поставил scanf (% c, & dummy); после каждого scanf a b и во внешнем цикле ввод был сохранен правильно, поэтому я правильно сохранил ввод, но я не могу понять, что заставляет код читать эти новые строки? Что-то не так в моем коде, из-за которого он читает эти новые строки? - person magiix; 06.02.2010
comment
Новые строки - это символы, как и любой другой символ. Следовательно, при посимвольной обработке потока вы встретите символы новой строки. См. Также: stackoverflow .com / questions / 1669821 / Попробуйте прочитать поток построчно (или построчно), и это, вероятно, будет проще. Например, cin >> b >> a; в начале, string buffer; cin >> buffer; во внешнем цикле и char temp = buffer[j] во внутреннем цикле, проверяя это buffer.size() == b, конечно. - person Jason Govig; 06.02.2010
comment
Спасибо, чувак, я понял концепцию из предоставленной вами ссылки. Большое спасибо: D. - person magiix; 06.02.2010

Почему у вас 0 индексов?

tempa.x=top.x+X[0];
tempa.y=top.y+Y[0];
tempa.time=map[top.x+X[0]][top.y+Y[0]];

Нитпик:

bool operator <(const node &s,const node &r)
{
    if(s.time>r.time)
    return true;
    else return false;
}

Разве это не более читабельно:

bool operator <(const node &s,const node &r)
{
    return (s.time>r.time);
}
person Kornel Kisielewicz    schedule 03.02.2010
comment
спасибо, но все еще не знаю, почему мой код выводит неправильные ответы для тестовых случаев в проблеме: S - person magiix; 03.02.2010
comment
Вы по-прежнему получаете неправильные ответы после изменения X [0] на X [i]? - person Peter Alexander; 03.02.2010

Вы используете X[0] и Y[0] вместо X[i] и Y[i] во внутреннем цикле.

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

Изменить: О, tempa.time должен равняться top.time плюс вес кромки, а не только вес кромки.

person Peter Alexander    schedule 03.02.2010
comment
ну, это моя первая реализация графа: D, так что если вы можете помочь мне улучшить его, добро пожаловать. В конце концов, это сформирует мою базу: D - person magiix; 03.02.2010
comment
Отредактировано с другим возможным исправлением. - person Peter Alexander; 03.02.2010