Особенности решения задач 25 и 26 компьютерного егэ по информатике
Скачать 1.03 Mb.
|
Особенности решения задач 25 и 26 компьютерного ЕГЭ по информатикеК.Ю. Поляков вебинар для учителей информатики г. Сочи 24 марта 2021 года 25. Пример(Демо-2021) Напишите программу, которая ищет среди целых чисел, принадлежащих числовому отрезку [174457; 174505], числа, имеющие ровно два различных натуральных делителя, не считая единицы и самого числа. Для каждого найденного числа запишите эти два делителя в таблицу на экране с новой строки в порядке возрастания произведения этих двух делителей. Делители в строке таблицы также должны следовать в порядке возрастания. 25. Общий подходПишем решение «в лоб». Если получили ответ, то СТОП. Оптимизируем. Переходим к шагу 2. Не нужно оптимизировать без необходимости! ! 25. Решение## var startN:= 174457; var endN:= 174505; for var x:=startN to endN do begin var count:= 0; var divs:= |0, 0|; for var d:=2 to x-1 do if x.Divs(d) then begin count += 1; if count > 2 then break; divs[count-1]:= d; end; if count = 2 then Println( divs[0], divs[1] ); end; var startN:= 174457000; var endN:= 174505000; Как уcкорить? ? 25. Ускорениеfor var d:=2 to x div 2 do begin ... end; Сложность алгоритма не меняется! O(N2) ! Делители в парах: Проблема: вещественное ! ! Проблема: полные квадраты! ! один делитель 25. Квадратный корень## var x := 100000000000; while x < 1000000000000 do begin var sqrtX := sqrt(x); if x <> sqrtX*sqrtX then Println(x, x-sqrtX*sqrtX); x := x + 1; end; Ошибка ± 1,5310–5! ! trunc? round? ceil? ? 25. Квадратный кореньfor var d:=2 to round(sqrt(x)) do begin ... end; 1) полный квадрат 5 round 2) два множителя с разностью 1 5 round round здесь округлит к меньшему! ! round(sqrt(x)) 25. Квадратный кореньvar d:= 2; while d*d <= x do begin ... d += 1; end; Без вещественных чисел: 25. Список делителей## var startN := 174457; var endN := 174505; for var x:=startN to endN do begin var divs:= new List for var d:=2 to round(sqrt(x)) do if x.Divs(d) then begin divs.Add(d); if d <> x div d then divs.Add(x div d); if divs.Count > 2 then break; end; if divs.Count = 2 then Println( divs[0], divs[1] ); end; var divs:= new List divs.Add(d); divs.Count divs.Count d*d <> x 25. Простые числаfunction IsPrime(x: integer): boolean; begin Result:= False; if x <= 1 then Exit; var d:= 2; while d*d <= x do begin if x.Divs(d) then Exit; d += 1; end; Result:= True; end; 25. Список простых чиселvar primes := new List for var i:=1 to 1000000 do if IsPrime(i) then primes.Add(i); Print( primes.Count ); Время 0,3 с! ! 25. Пример(Б.С. Михлин) Напишите программу, которая ищет среди целых чисел, принадлежащих числовому отрезку [194441; 196500] простые числа, оканчивающиеся на 93. 25. Решение «в лоб»var startN := 194441; var endN := 196500; for var x:=startN to endN do if (x mod 100 = 93) and IsPrime(x) then Println(x); 25. Оптимизацияvar startN:= 194493 ; var endN:= 196500; var x:= startN; while x <= endN do begin if IsPrime(x) then Println(x); x += 100; end; 194493 x += 100; первое, которое оканчивается на 93 25. ПримерРассматриваются целые числа, принадлежащих числовому отрезку [631632; 684934], которые представляют собой произведение двух различных простых делителей. Найдите такое из этих чисел, у которого два простых делителя больше всего отличаются друг от друга. 25. Решение «в лоб»var startN := 631632; var endN := 684934; var maxDiff := 0; var xMaxDiff := 0; for var x:=startN to endN do for var d:=2 to round(sqrt(x))-1 do if x.Divs(d) and IsPrime(d) and IsPrime(x div d) and (x div d - d > maxDiff) then begin maxDiff := x div d - d; xMaxDiff := x; end; Println( xMaxDiff, maxDiff ); var startN:= 63163200; var endN:= 68493400; Как уcкорить? ? 25. Оптимизацияfor var x:=startN to endN do for var d:=2 to round(sqrt(x))-1 do if x.Divs(d) then begin if IsPrime(x div d) and (x div d - d > maxDiff) then begin maxDiff := x div d - d; xMaxDiff := x; end; break; end; break; if x.Divs(d) then begin Пара «наименьший-наибольший» имеет наибольшую разность! ! IsPrime(d) первый d всегда простой! var primes := new List for var i:=1 to round(sqrt(endN)) do if IsPrime(i) then primes.Add(i); Список возможных меньших простых делителей: for var x:=startN to endN do foreach var d in primes do if x.Divs(d) then begin if IsPrime(x div d) and (x div d - d > maxDiff) then begin maxDiff := x div d - d; xMaxDiff := x; end; break; end; foreach var d in primes do var startN:= 63163200; var endN:= 68493400; 16,7 с! ! 17. ПримерНазовём натуральное число подходящим, если ровно два из его делителей входят в список (7, 11, 13, 19). Найдите все подходящие числа, принадлежащих отрезку [20 000; 30 000] В ответе запишите два целых числа: сначала количество, затем среднее арифметическое всех найденных чисел (только целую часть). Проблемы: ровно два из его делителей входят в список среднее арифметическое всех найденных чисел (сумма может быть очень велика!) 17. Ровно два делителяvar startN := 20000; var endN := 30000; var count := 0; for var x:=startN to endN do begin var divs := | integer(x mod 7 = 0), ord(x mod 11 = 0), ord(x.Divs(13)), 1-sign(x mod 19) |; if divs.Sum = 2 then count += 1; end; Println( count ); Как быть с суммой? ? int64 double var divs := | integer(x mod 7 = 0), ord(x mod 11 = 0), ord(x.Divs(13)), 1-sign(x mod 19) |; можно по-разному! 25. Пример(Статград) Найдите все натуральные числа, принадлежащие отрезку [289123456; 389123456] и имеющие ровно три нетривиальных делителя. Для каждого найденного числа запишите в ответе его наибольший нетривиальный делитель. Проблемы: долго считает… 25. Решение «в лоб»var startN := 289123456; var endN := 389123456; for var x:=startN to endN do begin var divs := new List for var d:=2 to x-1 do if x.Divs(d) then divs.Add(d); if divs.Count = 3 then Println( x ); end; Как уcкорить? ? 25. Только квадратыvar startN := 289123456; var endN := 389123456; for var sqrtX:=trunc(sqrt(startN)) to ceil(sqrt(endN)) do begin var x := sqrtX*sqrtX; var divs := new List for var d:=2 to x-1 do if x.Divs(d) then divs.Add(d); if divs.Count = 3 then Println( x ); end; Как уcкорить? ? Три (нечётное число) нетривиальных делителя – полный квадрат! for var sqrtX:=trunc(sqrt(startN)) to ceil(sqrt(endN)) do begin var x := sqrtX*sqrtX; 25. Основная теорема арифметикиЛюбое число единственным способом представляется в виде произведения простых чисел: Число нетривиальных делителей: Если = 3: 25. Только четвёртые степениvar startN := 289123456; var endN := 389123456; for var qX:=trunc(sqrt(sqrt(startN))) to ceil(sqrt(sqrt(endN))) do begin if IsPrime(qX) then Println( qX*qX*qX*qX ); end; 0,016 с! ! 25. Готовые функции(Демо-2021) Напишите программу, которая ищет среди целых чисел, принадлежащих числовому отрезку [174457; 174505], числа, имеющие ровно два различных натуральных делителя, не считая единицы и самого числа. Для каждого найденного числа запишите эти два делителя в таблицу на экране с новой строки в порядке возрастания произведения этих двух делителей. Делители в строке таблицы также должны следовать в порядке возрастания. 25. Модуль school## uses school; for var x:=174457 to 174505 do begin var divs := x.Divisors; if divs.Count = 4 then Println( divs[1], divs[2] ); end; x.Divisors; 25. Функциональный стиль## uses school; (174457..174505) .Select( x->x.Divisors ) .Where( x->x.Count = 4 ) .Select( x->(x[1],x[2]) ) .PrintLines; (174457..174505) последовательность чисел Презентации С.С. Михалковича: http://www.pascalabc.net/downloads/Presentations/Tutorials/Sequences.pdf http://www.pascalabc.net/downloads/Presentations/Tutorials/ProcFuncLambdas.pdf 25. Последовательностьfunction mySeq( a, b: integer ): sequence of integer ; begin while a <= b do begin yield a; a += 1; end; end; begin foreach var x in mySeq(10,20) do Print(x); end. sequence of integer yield a; 10 11 12 13 14 15 16 17 18 19 20 25. Функциональный стиль(10..20).Select( x->x.Divisors ).PrintLines; заменить каждый элемент последовательности на список его делителей [1,2,5,10] [1,11] [1,2,3,4,6,12] [1,13] [1,2,7,14] [1,3,5,15] [1,2,4,8,16] [1,17] .. все делители 10 11 12 13 25. Функциональный стиль(10..20).Select( x->x.Divisors ) .Where( x->x.Count = 4).PrintLines; отобрать те элементы списка, где количество делителей равно 4 [1,2,5,10] [1,2,7,14] [1,3,5,15] 10 14 15 25. Функциональный стиль(10..20).Select( x->x.Divisors ) .Where( x->x.Count = 4) .Select( x-> (x[1],x[2]) ).PrintLines; заменить каждый элемент списка на пару (кортеж), состоящую из двух нетривиальных делителей (2,5) (2,7) (3,5) 10 14 15 25. Пример(Б.С. Михлин) Напишите программу, которая ищет среди целых чисел, принадлежащих числовому отрезку [194441; 196500] простые числа, оканчивающиеся на 93. 25. Функциональный стиль## uses school; (194441..196500) .Where( x-> (x mod 100 = 93) and x.IsPrime ) .Println; x.IsPrime ## uses school; ( 194493 ..196500) .Step(100) .Where( x->x.IsPrime ) .Println; .Step(100) 194493 17. ПримерНазовём натуральное число подходящим, если ровно два из его делителей входят в список (7, 11, 13, 19). Найдите все подходящие числа, принадлежащих отрезку [20 000; 30 000] В ответе запишите два целых числа: сначала количество, затем среднее арифметическое всех найденных чисел (только целую часть). 25. Функциональный стиль## uses school; var selected := (20000..30000) .Select( x->x.Divisors ) .Select( x->(x.Last, integer(7 in x)+ integer(11 in x)+ integer(13 in x)+ integer(19 in x)) ) .Where( x-> x[1] = 2 ) .Select( x->x[0] ); Println( selected.Count, trunc(selected.Average) ); ord(...) 25. Функциональный стиль(70..91).Select( x->x.Divisors ).PrintLines; заменить каждый элемент последовательности на список его делителей [1,2,5,7,10,14,35,70] [1,71] [1,2,3,4,6,8,9,12,18,24,36,72] [1,73] [1,2,37,74] [1,3,5,15,25,75] [1,2,4,19,38,76] .. все делители 70 25. Функциональный стиль(70..91).Select( x->x.Divisors ) .Select( x->(x.Last, integer(7 in x)+ integer(11 in x)+ integer(13 in x)+ integer(19 in x) ) ) .Println; построить кортежи: ( число, количество делителей из [7,11,13,19]) (70,1) (71,0) (72,0) (73,0) (74,0) (75,0) (76,1) (77,2) .. 25. Функциональный стиль(70..91).Select( x->x.Divisors ) .Select( x->(x.Last, ...) ) .Where( x-> x[1] = 2 ) .Println; отобрать те, где количество делителей из списка (x[1]) равно 2: (77,2) (91,2) 25. Функциональный стиль(70..91).Select( x->x.Divisors ) .Select( x->(x.Last, ...) ) .Where( x-> x[1] = 2 ) .Select( x->x[0] ) .Println; оставить только сами числа (x[0]) 77 91 вывести количество и среднее: Println( selected.Count, selected.Average ); 2 84 25. Функциональный стиль## var z:= (20000..30000) .Select( x->(x, |7,11,13,19|.Count(d->x.Divs(d)) ) .Where( x-> x[1] = 2 ) .Select( x->x[0] ); Println( z.Count, z.Average ); ## var z:= (20000..30000) .Where( x->|7,11,13,19| .Count(d->x.Divs(d)) = 2 ); Println( z.Count, z.Average ); пары (число, кол-во делителей) 25. Пример(Статград) Найдите все натуральные числа, принадлежащие отрезку [289123456; 389123456] и имеющие ровно три нетривиальных делителя. Для каждого найденного числа запишите в ответе его наибольший нетривиальный делитель. 25. Функциональный стиль## uses school; (trunc(sqrt(sqrt(289123456))).. ceil(sqrt(sqrt(389123456)))) .Where( x->x.IsPrime ) .Select( x->x*x*x*x ) .Println; 26. Сортировка(Демо-2021) Раз в неделю создаёт архив файлов. Объём диска, может быть меньше, чем суммарный объём файлов. По заданной информации об объёме файлов пользователей и свободном объёме на архивном диске определите максимальное число пользователей, чьи файлы можно сохранить в архиве, а также максимальный размер имеющегося файла, который может быть сохранён в архиве, при условии, что сохранены файлы максимально возможного числа пользователей. 26. Решение в Excelhttps://kpolyakov.spb.ru/download/ege26.doc А. Сидоров: www.youtube.com/watch?v=LwTZAHsno0k 26. Решение## Assign(input, '26.txt'); var (V, n) := ReadInteger2; var a := ReadArrInteger(n); a.Sort; var i:=0; while (i <= a.High) and (V >= a[i]) do begin V -= a[i]; i += 1; end; i.Println; V += a[i-1]; while (i <= a.High) and (V >= a[i]) do i += 1; a[i-1].Print; a.Sort; 26. Сортировка## var A := | 27, 19, 21, 33 |; A.Sort; A.Println; 19 21 27 33 A.Sort( (x,y)-> x mod 10-y mod 10 ); A.Println; 21 33 27 19 A.Sort( (x,y)-> y mod 10-x mod 10 ); A.Println; 19 27 33 21 x mod 10-y mod 10 < 0 x < y = 0 x = y > 0 x > y .Sort переставляет элементы! ! 26. Сортировка21 41 33 19 19 33 41 21 ## uses school; var A := | 41, 19, 21, 33 |; A.Sort( (x,y)-> x.Digits.Sum-y.Digits.Sum ); A.Println; A.Sort( (x,y)-> y.Digits.Sum-x.Digits.Sum ); A.Println; x.Digits.Sum-y.Digits.Sum по сумме цифр ## var A := | 27, 19, 25, 33 |; A.Order.Println; 19 25 27 33 A.OrderDescending.Println; 33 27 25 19 .Order НЕ переставляет элементы и строит последовательность! ! A.Sort; A := A.Order.ToArray; ## var A := | 27, 19, 25, 33 |; A.OrderBy( x->x mod 10 ).Println; 33 25 27 19 A.OrderByDescending( x->x mod 10 ).Println; 19 27 25 33 по последней цифре x->x mod 10 21 41 33 19 19 33 41 21 по сумме цифр ## uses school; var A := | 41, 19, 21, 33 |; A.OrderBy( x->x.Digits.Sum ).Println; A.OrderByDescending( x->x.Digits.Sum ).Println; x->x.Digits.Sum по сумме цифр 26. Пример(Е. Джобс) В магазине проводят акция – каждый второй товар со скидкой 50%. При этом в акции участвуют только те товары, цены которых попадают в одну ценовую категорию. Каждая ценовая категория включает 500 целых значений: 1-500, 501-1000, 1001-1501 и т.д. Например, при наличии в чеке только позиций с ценами 300 и 1000 предложение акции не работает. Необходимо распределить товары в чеке таким образом, чтобы итоговая цена всех товаров была максимально выгодной для магазина. В качестве ответа вывести полученную сумму скидки для всего чека и конечную стоимость самого дорогого проданного по акции товара. В случае получения нецелых значений привести только целые части найденных чисел. 26. Решение на Pythonwith open("26-44.txt") as F: N = int(F.readline()) data = [] for i in range(N): data.append( int(F.readline()) ) data.sort() # ... продолжение следует 1 10 12 … 500 505 510 … 998 1004 … chunk chunk chunk 1 10 12 … 320 330 … 450 500 chunk скидки 26. Решение на Python# ... продолжение last = 500 discount, costMax = 0, 0 while data: chunk = [x for x in data if x <= last] if chunk: mid = len(chunk)//2 if mid > 0: discount += 0.5*sum(chunk[:mid]) costMax = 0.5*chunk[mid-1] data = data[len(chunk):] last+= 500 print( int(discount), int(costMax) ) ## Assign(input, '26-44.txt'); var N := ReadInteger; var data := ReadArrInteger(N); data.Sort; // ... продолжение следует # ... продолжение var (last, discount, costMax):= (500,0.0,0.0); while data.Count > 0 do begin var chunk := data.Where(x->x<=last).ToArray; if chunk.Count > 0 then begin var mid := chunk.Count div 2; if mid > 0 then begin discount += 0.5*chunk[:mid].Sum; costMax := 0.5*chunk[mid-1]; end; data := data.Where(x->x>last).ToArray; end; last += 500; end; Println( trunc(discount), trunc(costMax) ); 26. Пример(А. Кабанов) На складе лежат пакеты с углём различного веса и стоимости. Вес и стоимость записаны на каждом пакете как натуральные числа. Для транспортировки отбираются K пакетов с самой низкой ценой угля за единицу веса; при равной стоимости за единицу веса выбираются пакеты с большим весом. По заданной информации о пакетах с углём и количестве транспортируемых пакетов определите суммарный вес угля в отправленных пакетах и стоимость самого тяжёлого отправленного пакета. 26. Решение на Pythonwith open("26-k6.txt") as F: data = F.readlines() N, K = map(int, data[0].split()) del data[0] data = data[:N] pairs = [] for i in range(N): p = tuple( map(int, data[i].split()) ) pairs.append( p ) # ... продолжение следует строим массив пар (вес, стоимость) p = tuple( map(int, data[i].split()) ) 26. Решение на Python# ... продолжение pairs.sort( key = lambda x: (x[1]/x[0], -x[0]) ) selected = pairs[:K] print( sum( x[0] for x in selected ) ) weight = [x[0] for x in selected] ind = weight.index( max(weight) ) print( selected[ind][1] ) цена за единицу веса по убыванию веса суммарный вес стоимость пакета с наибольшим весом ## Assign(input, '26-k6.txt'); var (N, K):= ReadInteger2; var pairs := (1..N) .Select( i->ReadString.ToIntegers ) .Skip(1).ToArray; pairs.Sort( (x,y)-> x[1]/x[0]-y[1]/y[0] <> 0 ? sign(x[1]/x[0]-y[1]/y[0]) : y[0]-x[0] ); // ... продолжение следует цена за единицу веса условие если верно по убыванию веса если неверно # ... продолжение var selected := pairs[:K]; var weight := selected.Select( x->x[0] ).ToArray; weight.Sum.Println; var ind := weight.IndexOf( weight.Max ); selected[ind][1].Println; суммарный вес стоимость пакета с наибольшим весом 26. ПримерВ текстовом файле записан набор натуральных чисел. Гарантируется, что все числа различны. Необходимо определить, сколько в наборе таких пар нечётных чисел, что их среднее арифметическое тоже присутствует в файле, и чему равно наибольшее из средних арифметических таких пар. 26. Решение на Pythonwith open("26.txt") as F: N = int(F.readline()) data = [int(s) for s in F] data.sort() averages = [] for i in range(N-1): for j in range(i+1,N): if data[i] % 2 == 1 and data[j] % 2 == 1: s = data[i] + data[j] averages.append( s//2 ) averages.sort() # ... продолжение следует находим все подходящие средние 26. Решение на Python (плохое)# ... продолжение count = 0 ma = 0 for av in averages: if av in data: count += 1 ma = max( ma, av ) print(count, ma) for av in averages: if av in data: сложность O(N2) 26. Идея хорошего решения9, 10, 14, 13, 8, 11 Все пары нечётных чисел: (9, 13) (9, 11) (13, 11) 11 10 12 Отсортируем! ! 8, 9, 10, 11, 13, 14 10 11 12 Большее значение находится ближе к концу массива! ! 26. Решение на Python# ... продолжение selected = [] i = 0 for av in averages: while i < N and data[i] < av: i += 1 if i < N and data[i] == av: selected.append(av) print( len(selected), selected[-1] ) ## Assign(input, '26.txt'); var N:= ReadInteger; var data:= ReadArrInteger(N); data.Sort; var averages := new List for var i:=0 to data.High-1 do for var j:=i+1 to data.High do if data[i].IsOdd and data[j].IsOdd then averages.Add((data[i]+data[j]) div 2); averages.Sort; // ... продолжение следует ## Assign(input, '26.txt'); var N:= ReadInteger; var data:= ReadArrInteger(N); data.Sort; var averages := new List foreach var (x,y) in data.Combinations(2) do if x.IsOdd and y.IsOdd then averages.Add((x+y) div 2); averages.Sort; // ... продолжение следует foreach var (x,y) in data.Combinations(2) do if x.IsOdd and y.IsOdd then averages.Add((x+y) div 2); или так: # ... продолжение var selected := new List var i:= 0; foreach var av in averages do begin while (i < N) and (data[i] < av) do i += 1; if (i < N) and (data[i] = av) then selected.Add(av); end; Print( selected.Count, selected.Last ) БлагодарностиАвтор благодарит Алексея Богданова (Alex Danov) https://www.youtube.com/c/AlexDanov Станислава Михалковича https://pascalabc.net за полезные замечания и предложения. Конец фильмаПОЛЯКОВ Константин Юрьевич д.т.н., учитель информатики ГБОУ СОШ № 163, г. Санкт-Петербург kpolyakov@mail.ru |