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

Автор NKolotey, 13 лет назад, По-русски

Во время соревнований некогда эстествовать, поэтому чаще всего голова переключается в обычный императивный режим и руки пишут стандартные циклы, стандартные привычные процедуры, их и дебажить проще. Уже после контеста решил переписать решения с использованием функциональных возможностей C#, получилось вот что:

Задача A (поливание цветов)

        static void Main(string[] args)
        {
            int k = int.Parse(Console.ReadLine());
            if (k == 0)
                Console.WriteLine(0);
            else
            {
                var q = from s in Console.ReadLine().Split()
                        let i = int.Parse(s)
                        orderby i descending
                        select i;

                int sum = 0;
                int count = q.TakeWhile(x => (sum += x) < k).Count();
                Console.WriteLine(sum < k ? -1 : count + 1);
            }
        }

Стиль записи var q = from x in ... join ... where ... orderby ... select ... похож на язык sql запросов, по сути выражение и является запросом, только источником данных служит не СУБД, а обычная IEnumerable либо IQueriable коллекция, в данном случае массив строк, получившихся в результате распиливания входной строки Console.ReadLine().Split(). Это же самое можно было записать малость менее читабельно: var q = Console.ReadLine().Split().Select(x => int.Parse(x)).OrderByDescending(x => x); хотя найдутся и те, кому такой поезд из кучи сцепленных вагончиков более по душе. Например, вебстеры, работающие с jquery, постоянно пишут клиент-сайд скрипты в очень похожем стиле. Главное преимущество такой записи — компактность. Но ещё и наличие некоторых дополнительных overload'ов методов с дополнительным аргументом — указывающем на позицию переданного в лямбда-функцию элемента коллекции, этим можно воспользоваться в третьей задаче.

Задача B (марсианские часы)

        static void Main(string[] args)
        {
            string[] hm = Console.ReadLine().Split(':');

            Func<string, int, int> s2i = (s, b) =>
            {
                var a = s.Select(c => (c <= '9') ? c - '0' : c - 'A' + 10).ToArray();
                if (a.Any(x => x >= b)) return 60;
                return a.Aggregate((acc, x) => acc < 60 ? acc * b + x : acc);
            };

            var possible = Enumerable.Range(1, 100).Where(x => s2i(hm[0], x) <= 23 && s2i(hm[1], x) <= 59).ToArray();

            if(!possible.Any())
                Console.WriteLine(0);
            else if(possible.Last() == 100)
                Console.WriteLine(-1);
            else
                foreach(var x in possible)
                    Console.Write("{0} ", x);
        }

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

Задача C (деление на команды)

        static void Main(string[] args)
        {
            int n = int.Parse(Console.ReadLine());
            var q = Console.ReadLine().Split().Select((x, pos) => new { i = pos + 1, a = int.Parse(x) })
                .OrderBy(x => x.a).ToArray();

            Console.WriteLine((q.Length + 1) / 2);
            foreach (var x in q.Where((x, pos) => pos % 2 == 0))
                Console.Write("{0} ", x.i);
            Console.WriteLine();

            Console.WriteLine(q.Length / 2);
            foreach (var x in q.Where((x, pos) => pos % 2 == 1))
                Console.Write("{0} ", x.i);
            Console.WriteLine();
        }

.Select((x, pos) => ...) и .Where((x, pos) => ...) имеют одним из аргументов лямбду, принимающую на вход не только сам item, но и его позицию. В linq стиле (который похож на sql запрос, то есть начинается с from x in collection ...) эту текущую позицию получить, к сожалению, нельзя, либо я не знаю как это можно сделать. Сначала генерится коллекцию пар { позиция, значение }: Select((x, pos) => new { i = pos + 1, a = int.Parse(x) }) затем сортируется OrderBy(x => x.a), ну и т.к. результатом потребуется воспользоваться дважды (один раз для вывода чётных позиций, другой для нечётных), поэтому нужно материализовать во что-нибудь ToArray().

В четвёртой задаче места linq'у у меня вообще не нашлось, а пятую я не сделал.

Полный текст и комментарии »

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

Автор NKolotey, 13 лет назад, По-русски

Сразу примечение: я знаю, что она не идентична классической "о назначениях", просто не придумал о чём же она, кроме как о назначениях. Помогите пожалста либо доказать, что жадный подход нормально работает, либо свести задачу к какой-нибудь известной.

Суть вот в чём: есть разноцветные ленточки единичной ширины, их можно переплетать, получая прямоугольный рисунок. Если в некой клетке одна из лент A идёт поверх ей перпендикулярной B, то цвет у клетки, соответствующей пересечению, будет цветом ленты A. Если же A подныривает под B, то значит цвет у клетки будет цветом ленты B.


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

Ограничения по ширине и высоте ~1500x1500, ограничение по числу цветов ~40, по времени что-нибудь человекоразумное.

Я решаю простым способом: выдвигаю предположение, что самый левый верхний квадратик принадлежит горизонтальной ленточке. В таком случае все квадратики другого цвета в верхней строке должны задавать цвета вертикальных ленточек. Запихиваю их в очередь и по очереди проверяю каждый соответствующий столбец, условно говоря, поворачивая задачу на 90 градусов.

Если в какой-то момент очереди обработки опустели, значит задача слегка редуцировалась. Например имеем такой вот инпут:

   ???????
 
?  AAAAAAB
?  DXXXXXB
?  DXYYYXB
?  DCCCCCC
?  DXXXXXB
?  DEEEEEE

Предполагаем, что 'A' в левом вехрнем углу задаёт цвет первой горизонтальной ленты. Тогда 'B' в последнем столбце должна задать цвет последней, седьмой, вертикальной ленты. Обрабатывая эту вертикальную ленту, в четвёртой строке натолкнёмся на 'C' - значит это цвет четвёртой горизонтальной ленты, и 'E' в последней, шестой, строке, значит это цвет последней горизонтальной ленты. Обрабатывая горизонтальные ленты 4 и 6 в любом порядке выясним, что у первого столбца должен быть цвет 'D'. Обрабатывая первый столбец ничего уже полезного не выясним - в первой строке цвет был 'A', но это мы и раньше уже знали.

Задача слегка редуцировалась:

   D?????B
                  ?????
A  AAAAAAB
?  DXXXXXB     ?  XXXXX
?  DXYYYXB  => ?  XYYYX
C  DCCCCCC     ?  XXXXX
?  DXXXXXB
E  DEEEEEE


Если она не редуцировалась, значит где-то упёрлись в противоречие, стоит вернуться в начало и попробовать связать 'A' не со строкой, а со столбцом. Если и этот вариант не сработал, то сразу же печать ругательного сообщения и завершение процесса.

Такой вот приблизительно подход приходит на ум сразу, но не получается доказать, что он всегда будет находить вариант сопоставления корректной картинке каких нибудь цветов для лент. Поскольку после редукции алгоритм уже не возвращается назад, значит гипотетически возможна такая ситуация, когда неверно выбранное направление на шаге 1 через несколько редукций заведёт в тупик. Возможно ли придумать контрпример, на котором алгоритм сфэйлит?

PS: возможно, что неправильный, имплементэйшн тут. http://codepaste.net/ob8imc


Полный текст и комментарии »

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