Во время соревнований некогда эстествовать, поэтому чаще всего голова переключается в обычный императивный режим и руки пишут стандартные циклы, стандартные привычные процедуры, их и дебажить проще. Уже после контеста решил переписать решения с использованием функциональных возможностей 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'у у меня вообще не нашлось, а пятую я не сделал.