Пожалуйста, прочтите новое правило об ограничении использования AI-инструментов. ×

Блог пользователя ruban

Автор ruban, 12 лет назад, По-русски
Много раз пытался сдать 198B - прыжки по стенам. Вроде бы вполне осмысленное решение  -
  • код ниже — получает RE на 12 тесте. Но это не самое интересное — я поглядел 12 тест и
    обнаружил, что на том же тесте с длиной прыжка 56465 все нормально, а при длине 56466 — уже RE. Но и это не все. Мистика (для меня) состоит в том, что при чистке кода я обнаружил, что при удалении ненужных (нигде не используемых) переменных это самое число 56465 изменяется на несколько сотен. Я, естественно, понимаю, что один дурак может задать столько вопросов, что тысяча мудрецов не ответит, но все же, может кто что подскажет... (Для меня это — третья по счету задача с использованием рекурсии, а может и не в рекурсии дело). Заранее благодарен за подсказку.

program pr1;

{$APPTYPE CONSOLE} var n,k,i,j:longint; t:array[0..1,0..1000000] of int64; flag:boolean; marked: array [0..1,0..1000000] of boolean; sss:array[0..1] of string;

procedure dfs(num,pl,shag:longint); var i,p:longint;

begin if shag<pl then begin marked[num,pl]:=true; inc(shag); t[num,pl]:=shag; if not marked[1-num,pl+k] and (sss[1-num,pl+k]='-')and(pl<=n) then dfs(1-num,pl+k,shag); if not marked[num,pl+1] and (sss[num,pl+1]='-')and(pl<n+k) then dfs(num,pl+1,shag); if not marked[num,pl-1] and (sss[num,pl-1]='-')and(pl>1) then dfs(num,pl-1,shag); end;

end;

begin {assign (input,'input.txt');reset(input); assign (output,'output.txt'); rewrite(output); } readln(n,k); readln(sss[0]);readln(sss[1]); for i:=1 to 100000 do sss[0]:=sss[0]+'-'; for i:=1 to 100000 do sss[1]:=sss[1]+'-'; for i:=0 to n+k do begin t[0,i]:=1000000; t[1,i]:=1000000; end; t[0,1]:=0; dfs(0,1,0); flag:=false; for i:=n+1 to n+k do if (t[1,i]<1000000) or (t[0,i]<1000000) then begin flag:=true; end; if flag then write('YES') else write('NO'); close(output); end.

  • Проголосовать: нравится
  • -8
  • Проголосовать: не нравится

»
12 лет назад, # |
Rev. 3   Проголосовать: нравится +6 Проголосовать: не нравится

Установите размер стека с помощью директивы {$MAXSTACKSIZE [размер в байтах]}. Я обычно ставлю 128МБ (правда, для Java).

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится -6 Проголосовать: не нравится
    Прямо как в анекдоте "Папаша, Вы будете смеяться, но Ваша Сонечка таки тоже умерла".

    Так вот — попробовал поставить очень большую константу — 2М — эффект тот же, те же самые 56465 — граница. При меньших размерах стека граница становится меньше.

    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится +5 Проголосовать: не нравится

      Какого черта Вы жлобитесь? Поставьте сразу 128М. Там дефолтный размер-то, вроде бы, 1М.

      • »
        »
        »
        »
        12 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Все то же. Или даже еще хуже. Может и не в рекурсии дело????

        • »
          »
          »
          »
          »
          12 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится

          Я сейчас посмотрел: ошибка 216, которую выкидывает Ваша программа (и про которую Вы не удосужились погуглить) — повреждение памяти.

          • »
            »
            »
            »
            »
            »
            12 лет назад, # ^ |
              Проголосовать: нравится -10 Проголосовать: не нравится

            Вопрос первый — из-за чего? И второй — и что делать??? (Я не очень-то и программист).

            • »
              »
              »
              »
              »
              »
              »
              12 лет назад, # ^ |
                Проголосовать: нравится +5 Проголосовать: не нравится

              Вопрос третий: а кто Вы? Вопрос четвертый: почему Вы за полтора года участия на Codeforces так и не стали очень-программистом? Вопрос пятый: почему бы самостоятельно не подумать?

    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится +11 Проголосовать: не нравится

      Ой, а можно полную версию анекдота?

      • »
        »
        »
        »
        12 лет назад, # ^ |
          Проголосовать: нравится -12 Проголосовать: не нравится

        Сначала разберусь с проблемой, а потом — анекдот. Утром — деньги, вечером — стулья.

»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Кажется в Delphi есть директива MINSTACKSIZE (кроме MAXSTACKSIZE). Её и нужно установить

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Спасибо, но пока воз и ныне там — 56465. (При любых размерах стека)

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Я почитал: первоначально размер стека устанавливается в значение MINSTACKSIZE, потом, если его не хватает, постепенно увеличивается, но не более, чем до MAXSTACKSIZE.

    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Все то же - 56465. Наверное, не в стеке дело. Я, надо полагать, могу показаться весьма занудным, но хочется знать, в чем дело.
»
12 лет назад, # |
Rev. 2   Проголосовать: нравится +3 Проголосовать: не нравится

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

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится -10 Проголосовать: не нравится
    Проще всего - глянуть посылку 1834501.
    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится +13 Проголосовать: не нравится

      проще всего отредактировать запись, ну или же сделать ссылку на посылку 1834501

»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Я пишу на С# и тоже всегда ловлю RE на DFSах. Вот здесь http://codeforces.net/blog/entry/79#comment-94622 я описал подробно почему это случается, и почему это нельзя пофиксить своими силами, видимо тут то же самое. Пока никакой реакции, так что могу посоветовать менять по возможности на BFS, писать нерекурсивную реализацию или учить что-то другое (плюсы)

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    А на Codeforces код разве запускается как частично доверенный? Мне казалось, что там делается так: клонируется виртуальная машина, на этом клоне запускается решение, а потом этот клон убивается. При такой конфигурации вроде бы вообще нет резона ограничивать "доверенность".

    ИМХО, дело в том, что на CF стоит не .NET Framework, а Mono.

    Я, кстати, через вкладку "Запуск" запускал исходники, читающие ключи реестра (чтобы узнать, какие процессоры стоят на серверах).

»
12 лет назад, # |
Rev. 6   Проголосовать: нравится +16 Проголосовать: не нравится
Я нашел проблему:
1) Поставь в начале {M 10000000}
2) Забивай масив sss от 1 до 10^6.

1834680

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +2 Проголосовать: не нравится
    Огромное спасибо за реальную помощь - действительно сдалась. А теперь вопрос знающим людям - почему???? Что это за такие особенности языка, разобраться с которыми составило такую неслабую проблему! И напоследок - анекдот (желательно рассказывать, а не читать):

    Парень женился на старшей из трех сестер. Все безмерно рады. Через год приезжает — "Какое горе, жена померла". Ну ему по традиции дали в жены среднюю. Через год тот снова появляется — и прямо с порога : "Папаша , вы таки будете смяться, но Ваша Сонечка таки тоже померла! "

    • »
      »
      »
      12 лет назад, # ^ |
      Rev. 3   Проголосовать: нравится +4 Проголосовать: не нравится

      Просто у тебя в рекурсии будет использовать елементы строки которых нет... естественно будет рантайм ерор

    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится +4 Проголосовать: не нравится

      А я анекдот не понял.

    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится +4 Проголосовать: не нравится

      "по традиции дали в жены среднюю" О_о Какая-то сильно повышенная смертность жён, раз уже традиции появились.

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +5 Проголосовать: не нравится

    Да, и чуть не забыл, с меня шоколадка. Будете у нас в Новосибирске на Всесибирской (где-то в ноябре )- тут же отдам. Вторая шоколадка тому, кто объяснит, в чем же дело. Важно ведь разобраться, чтобы в дальнейшем подобных проблем не было.

»
12 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Так вот!!! Дело вовсе не в стеке. Можно вообще про стек ничего не описывать. Достаточно просто к строке добавить не 10^5, а 198000 пробелов (195000 не хватает)- и все OK. Но почему??? Как все-таки работает рекурсия. Кто-нибудь понимает? Вроде бы нигде в проге к далеким элементам строки я не обращаюсь.

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    (sss[1-num,pl+k]='-') у тебя выходит за 100000 ...

    • »
      »
      »
      12 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Не понял... У меня же к строке приписывается 100000 пробелов, а при DFSе я слежу, чтобы pl+k<=n+k (в коде - pl<=n). Так что вроде все нормально, по-моему.
      • »
        »
        »
        »
        12 лет назад, # ^ |
        Rev. 3   Проголосовать: нравится +2 Проголосовать: не нравится
        Оно у вас сразу вышло за границы и выбило рантайм, а потом уже проверило,'чувак' это делфи :)
        Попробуйте сразу проверить `pl<=n`, а потом уже делать следующие проверки, должно пройти ...
        

        UPD.Всё верно, прошло, вот этому подтверждение 1835200

      • »
        »
        »
        »
        12 лет назад, # ^ |
        Rev. 2   Проголосовать: нравится +6 Проголосовать: не нравится

        Omelianenko правильно говорит, что нужно вначале проверить, что не выходит за границы массива, а потом уже смотреть что там в массиве лежит.

        Суть в том, что оператор (A and B) работает по такому принципу: вначале проверяется A, если оно верно, то проверяется B, иначе B не проверяется, ибо смысла в этом нет. Поэтому важно вначале проверить выход за границы массива, чтобы если мы вышли, то второе условие не проверялось, если же переставить местами, то вначале мы проверяем ячейку массива, которой фактически нет, а дальше мы уже ничего не будем проверять, потому что у нас вылетит рантайм, ибо мы не можем проверять того чего нет.

        Оператор (A or B), кстати, тоже работает интересно: если A — true, то смысла проверять B нету, поэтому вот такой код хорошо сработает:

        t:=-1;
        for i:= 0 to n-1 do
        if (t<0 or a[t]<a[i]) then t:=i;
        

        Нужно очень тщательно следить за тем, не выходите ли вы за границы массива, причём не просто лепить проверки, а в каждой строчке кода понимать, какие значения могут принимать индексы массива, к которым обращаемся.

        И ещё хочу кое-что сказать. В С++ проверки за границы массива выключены по умолчанию (не знаю, можно ли их включить), поэтому если создать массив int a[10] (это массив int размера 10, с индексами от 0, то есть 0..9) и обращаться например к 20-му элементу массива a[20], то может так произойти, что рантайма не выскочит, хоть может и выскочить, зависит это от того, что будет храниться в ячейке памяти отстоящей на 20*4 байт от начала массива a, если будет ваша программа, то права на эту ячейку памяти у неё есть и она просто возьмёт оттуда данные (понятно, что совсем некорректные и ненужные и вообще чужие), и ещё хуже будет если она туда запишет данные, тем самым испортив какие-то чужие данные, если же эта ячейка памяти вообще не принадлежит вашей программе, то будет сгенерирован рантайм ошибка доступа. В паскале так тоже может происходить, но там по умолчанию включена проверка на выход за границы массива, но какая-то директива (не помню какая, можно погуглить) отключает эту проверку. Так что, ещё раз говорю, нужно быть осторожным с границами, иначе можно не просто рантайм получить, а вообще неадекватное поведение программы (например можно случайно в результате выхода за границы массива и обращения к ячейке памяти, в которой лежит например значение переменной n, от которой зависит текущий цикл, затереть это значение n и уйти в бесконечный цикл).

        • »
          »
          »
          »
          »
          12 лет назад, # ^ |
            Проголосовать: нравится +3 Проголосовать: не нравится

          Директива для отключения проверки выхода в паскале — {$R-}

        • »
          »
          »
          »
          »
          12 лет назад, # ^ |
            Проголосовать: нравится +1 Проголосовать: не нравится
          Еще раз спасибо за советы. Так вот, после экспериментов выяснилось, что обычный Delphi, со стандартными настройками при обращении к n+1-му  элементу массива все еще нормально (он=0), а к n+2 - уже RE. Со строкой все еще веселее - где-то 16000 элементов с ord=0, а дальше - RE. Именно эти 16000 элементов непонятного происхождения меня и сбили с толку - так бы я, пожалуй, легко сам обнаружил бы выход своей кривоватой проги за пределы строки.   Обычный же Pascal просто начинает обращаться к "левым" ячейкам памяти, что может привести к зависанию компа - этот эффект мне объяснили очень давно - лет двадцать. Капризность Delpi (оно и к лучшему) при работе с массивами тоже давно знакома. А вот столь странный эффект при работе со строкой обнаружен только сейчас. Буду знать. Еще раз всем спасибо за подсказки.
          • »
            »
            »
            »
            »
            »
            12 лет назад, # ^ |
              Проголосовать: нравится 0 Проголосовать: не нравится

            Может, строки в Delphi, как C++ vector-ы, автоматически ресайзятся, когда это необходимо? Вот и получается несколько нулей про запас в конце.

»
12 лет назад, # |
  Проголосовать: нравится -7 Проголосовать: не нравится

Попробуй написать в самом верху кода {$M 128000000}