Таблици с числа
Проблем
Три седмици, три масива
Кафенето записва колко кафета е продало всеки работен ден. За една седмица масивът от миналия урок е точно каквото трябва. За три седмици правиш три масива:
int[] week1 = { 42, 38, 51, 47, 60 };int[] week2 = { 45, 40, 49, 52, 63 };int[] week3 = { 39, 44, 50, 48, 58 };
int tuesday = week1[1] + week2[1] + week3[1];Console.WriteLine("Вторник: " + tuesday);Вторник: 122Сега управителят пита колко е продадено във всички вторници за последната година. Това са 52 седмици, значи 52 масива и сбор с 52 члена. А цикъл не може да мине през week1, week2, week3: това са различни имена, не индекси.
Данните са таблица: седмиците са редове, дните са колони. Трябва ти масив, който е таблица.
Идеята
Двумерен масив: ред и колона
Масив, в който всяка стойност има ред и колона, се казва двумерен масив. Често го наричат и матрица.
int[,] sales ={ { 42, 38, 51, 47, 60 }, { 45, 40, 49, 52, 63 }, { 39, 44, 50, 48, 58 }};Console.WriteLine(sales[1, 0]);Console.WriteLine(sales.GetLength(0) + " седмици, " + sales.GetLength(1) + " дни");453 седмици, 5 дниint[,]значи таблица от цели числа. Запетаята в скобите казва, че индексите са два. Всеки вътрешен{ … }е един ред.sales[1, 0]е редът с индекс 1 (втората седмица) и колоната с индекс 0 (понеделник). Първо е редът, после колоната. Броенето и тук е от 0.sales.GetLength(0)е броят на редовете,sales.GetLength(1)е броят на колоните. Числото в скобите е кое измерение питаш: 0 за редовете, 1 за колоните.
Такава таблица се казва правоъгълен масив: всички редове са с еднаква дължина.
Празна таблица създаваш с размерите ѝ: new int[52, 5] е 52 реда по 5 числа, всичките 0.
По-точно, за любопитните
В C# има и втори вид: назъбен масив (jagged), int[][]. Той е масив от масиви и всеки ред може да е с различна дължина. Индексите са в отделни скоби, shifts[1][0], а дължината на ред е shifts[1].Length:
int[][] shifts = new int[3][];shifts[0] = new int[] { 8, 8 };shifts[1] = new int[] { 6, 6, 6 };shifts[2] = new int[] { 12 };Console.WriteLine(shifts[1].Length + " " + shifts[2].Length);3 1Ползвай int[,], когато данните са истинска таблица (седмици и дни, редове и места в зала). Ползвай int[][], когато редовете са с различна дължина.
sales.Length при int[,] е броят на всички клетки (3 · 5 = 15), а не броят на редовете. sales.Rank е броят на измеренията (2).
Виж го
Цялата таблица на екрана
За да минеш през всяка клетка, ти трябват два цикъла един в друг. Вложените цикли вече ги знаеш от звездичките: външният избира реда, вътрешният минава през колоните на този ред.
for (int week = 0; week < sales.GetLength(0); week++){ for (int day = 0; day < sales.GetLength(1); day++) { Console.Write($"{sales[week, day],5}"); } Console.WriteLine();} 42 38 51 47 60 45 40 49 52 63 39 44 50 48 58Външният цикъл се завърта 3 пъти, по веднъж за всяка седмица. При всяко негово завъртане вътрешният се завърта 5 пъти. Общо 15 клетки, в реда, в който четеш таблица: отляво надясно, отгоре надолу.
$"{x,5}" печата числото в поле от 5 знака, подравнено вдясно. Така колоните застават една под друга. След всеки ред WriteLine() минава на нов ред.
Пробвай
Коя клетка?
В дневника всеки ред е ученик, а всяка колона е предмет.
Какво ще отпечата програмата?
int[,] grades ={ { 5, 3, 4, 6 }, { 3, 4, 6, 5 }, { 6, 2, 5, 4 }};Console.WriteLine(grades[1, 2]);Първият индекс е редът: 1 е вторият ученик, { 3, 4, 6, 5 }. Вторият индекс е колоната: 2 е третият предмет. Оценката е 6. Числото 2 излиза, ако объркаш реда с колоната, а 3, ако броиш от 1.
Идеята
Сума по редове и по колони
Колко кафета са продадени всяка седмица? Това е сумата на всеки ред. Сумата започва от 0 за всеки нов ред, затова total е вътре във външния цикъл:
for (int week = 0; week < sales.GetLength(0); week++){ int total = 0; for (int day = 0; day < sales.GetLength(1); day++) { total += sales[week, day]; } Console.WriteLine("Седмица " + (week + 1) + ": " + total);}Седмица 1: 238Седмица 2: 249Седмица 3: 239int[,] sales ={ { 42, 38, 51, 47, 60 }, { 45, 40, 49, 52, 63 }, { 39, 44, 50, 48, 58 }};for (int week = 0; week < sales.GetLength(0); week++){ int total = 0; for (int day = 0; day < sales.GetLength(1); day++) { total += sales[week, day]; } Console.WriteLine("Седмица " + (week + 1) + ": " + total);}Стъпка по стъпка, с думи
- Таблицата: 3 седмици (редове) по 5 дни (колони).
- week = 0: total започва отначало, от 0.
- Вътрешният цикъл минава през дните: 42 + 38 + 51 + 47 + 60 = 238.
- Отпечатва „Седмица 1: 238“.
- week = 1: total започва отначало, от 0.
- Вътрешният цикъл минава през дните: 45 + 40 + 49 + 52 + 63 = 249.
- Отпечатва „Седмица 2: 249“.
- week = 2: total започва отначало, от 0.
- Вътрешният цикъл минава през дните: 39 + 44 + 50 + 48 + 58 = 239.
- Отпечатва „Седмица 3: 239“.
Колко е продадено в понеделник, във вторник и т.н.? Това е сумата на всяка колона. Таблицата е същата, сменя се само редът на циклите: отвън е денят, отвътре са седмиците. Ще го допишеш в следващата карта.
Кой цикъл е отвън, решава какво сумираш. Индексите в sales[week, day] не се местят: първо е винаги редът.
Пробвай
Сума по дни
Външният цикъл минава през дните, вътрешният през седмиците. Какво трябва да пише на празното място?
string[] days = { "Пн", "Вт", "Ср", "Чт", "Пт" };
for (int day = 0; day < sales.GetLength(1); day++)
{
int total = 0;
for (int week = 0; week < sales.GetLength(0); week++)
{
total += sales[, day];
}
Console.WriteLine(days[day] + ": " + total);
}week. Редът е седмицата, затова на първо място е week. Денят стои на място, докато вътрешният цикъл минава през трите седмици. Във вторник излиза 38 + 40 + 44 = 122, същото като в първата карта, но без 52 имена.
Пробвай
Намери грешката
Залата има 3 реда по 5 места: 1 е заето, 0 е свободно. Свободните места са 7, а програмата казва 5. На кой ред е грешката?
Избери реда с грешката.
Вътрешният цикъл е за местата на реда, а броят им е hall.GetLength(1) (5). hall.GetLength(0) е броят на редовете (3), затова последните две места на всеки ред не се проверяват. Компилаторът не вижда такава грешка: и двете числа са int.
Идеята
Резервация: хората броят от 1
Зрителят иска ред 3, място 2. За него първият ред е ред 1. За масива той е ред 0. Затова от числата, които въвежда човек, вадиш 1, преди да ги ползваш като индекси:
int row = int.Parse(Console.ReadLine());int seat = int.Parse(Console.ReadLine());if (hall[row - 1, seat - 1] == 1){ Console.WriteLine("Мястото е заето.");}else{ hall[row - 1, seat - 1] = 1; Console.WriteLine("Запазено: ред " + row + ", място " + seat);}32Запазено: ред 3, място 212Мястото е заето.hall е залата от предната карта. Клетката се променя като обикновена променлива: hall[row - 1, seat - 1] = 1;.
По-точно, за любопитните
- Ако някой поиска ред 7, индексът 6 е извън таблицата и програмата спира с
IndexOutOfRangeException, също като при обикновен масив. Провери числата, преди да ги ползваш:row >= 1 && row <=hall.GetLength(0), и същото за мястото.
- Тук всяко число е на отделен ред. Как да прочетеш цял ред числа наведнъж (
0 1 1 0), ще видиш в следващия урок за текста.
Упражнение
Сметки по таблица
Реши задачите в редактора си. Таблиците напиши направо в кода.
1. Диагонали и редове. За таблицата 2 × 2 по-долу намери сумата по главния диагонал (отгоре вляво надолу вдясно), по другия диагонал и сумите на първия и на втория ред. На главния диагонал индексът на реда и на колоната са равни. На другия диагонал колоната е n - 1 - ред.
1 23 4Главен диагонал: 5Втори диагонал: 5Първи ред: 3Втори ред: 72. Обърни таблицата. Продажбите са записани по седмици (редове) и дни (колони). Счетоводството ги иска по дни и седмици. Направи нова таблица, в която редовете стават колони. Това се казва транспониране. Новата таблица е с размери колони × редове, а клетката [r, c] отива на [c, r].
42 38 51 45 40 49 42 45 38 40 51 49Упражнение
Лабиринти и съкровище
3. Колко пътя има? Лабиринтът е таблица: 0 е проход, 1 е стена. Тръгваш от горния ляв ъгъл и стигаш до долния десен, като се движиш само надясно или надолу. Колко различни пътя има?
0 0 0 00 1 0 00 0 0 11 0 0 0Пътища: 3Подсказка
Направи втора таблица ways със същия размер. В нея пиши по колко начина се стига до всяка клетка. До началото се стига по 1 начин. До стена по 0. До всяка друга клетка се стига или отгоре, или отляво, затова броят е сборът от клетката над нея и клетката вляво от нея (ако ги има). Попълни таблицата ред по ред, отгоре надолу. Отговорът е в последната клетка.
4. Най-краткият път. Трудна задача. Сега може да се движиш и в четирите посоки: нагоре, надолу, наляво и надясно. С колко стъпки най-малко се стига от горния ляв до долния десен ъгъл? Ако няма път, отпечатай „Няма път“.
0 0 0 0 01 1 1 1 00 0 0 0 00 1 1 1 10 0 0 0 0Най-кратък път: 16 стъпкиПодсказка
Пусни „вълна“ от началото. Направи таблица dist, пълна с -1 (още не сме стигнали дотам), и сложи 0 в началото. После повтаряй: намери всички клетки с dist равно на текущата стъпка и на свободните им съседи, до които още не сме стигнали, запиши стъпка + 1. Вълната расте с една стъпка на всяко завъртане. Спри, когато стигне целта или когато нищо не се е променило (тогава няма път). Четирите посоки са удобни като два масива: int[] dr = { -1, 1, 0, 0 }; и int[] dc = { 0, 0, -1, 1 };. Съседът в посока k е на ред r + dr[k] и колона c + dc[k].
Още задачи
- Намери съкровището. Игра на карта, която е таблица: 0 е път, 1 е препятствие, 2 е съкровището. Играчът започва в горния ляв ъгъл. На всеки ход отпечатай картата (
Pза играча,#за препятствие,.за път) и прочети команда като число: 8 нагоре, 2 надолу, 4 наляво, 6 надясно (както на цифровата клавиатура). Ако ходът води извън картата или в препятствие, играчът остава на място. Когато стъпи на съкровището, отпечатай „Намери съкровището с … хода!“. - Ред с най-много продажби. За таблицата
salesнамери коя седмица е била най-силна и кой ден от седмицата е най-силен средно. - Свободни места подред. За залата
hallнамери ред, в който има поне 3 свободни места едно до друго, и отпечатай номера на реда и на първото място.
Тест