Към съдържанието
wannacode

Таблици с числа

Проблем

Три седмици, три масива

Кафенето записва колко кафета е продало всеки работен ден. За една седмица масивът от миналия урок е точно каквото трябва. За три седмици правиш три масива:

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) + " дни");
Изход
45
3 седмици, 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]);
Отговор

Идеята

Сума по редове и по колони

Колко кафета са продадени всяка седмица? Това е сумата на всеки ред. Сумата започва от 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: 239

int[,] 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);
}
Стъпка по стъпка, с думи
  1. Таблицата: 3 седмици (редове) по 5 дни (колони).
  2. week = 0: total започва отначало, от 0.
  3. Вътрешният цикъл минава през дните: 42 + 38 + 51 + 47 + 60 = 238.
  4. Отпечатва „Седмица 1: 238“.
  5. week = 1: total започва отначало, от 0.
  6. Вътрешният цикъл минава през дните: 45 + 40 + 49 + 52 + 63 = 249.
  7. Отпечатва „Седмица 2: 249“.
  8. week = 2: total започва отначало, от 0.
  9. Вътрешният цикъл минава през дните: 39 + 44 + 50 + 48 + 58 = 239.
  10. Отпечатва „Седмица 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);
}

Пробвай

Намери грешката

Залата има 3 реда по 5 места: 1 е заето, 0 е свободно. Свободните места са 7, а програмата казва 5. На кой ред е грешката?

Избери реда с грешката.

Идеята

Резервация: хората броят от 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);
}
Вход
3
2
Изход
Запазено: ред 3, място 2
Вход
1
2
Изход
Мястото е заето.

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 2
3 4
Изход
Главен диагонал: 5
Втори диагонал: 5
Първи ред: 3
Втори ред: 7

2. Обърни таблицата. Продажбите са записани по седмици (редове) и дни (колони). Счетоводството ги иска по дни и седмици. Направи нова таблица, в която редовете стават колони. Това се казва транспониране. Новата таблица е с размери колони × редове, а клетката [r, c] отива на [c, r].

Таблицата
42 38 51
45 40 49
Изход
42 45
38 40
51 49

Упражнение

Лабиринти и съкровище

3. Колко пътя има? Лабиринтът е таблица: 0 е проход, 1 е стена. Тръгваш от горния ляв ъгъл и стигаш до долния десен, като се движиш само надясно или надолу. Колко различни пътя има?

Лабиринтът
0 0 0 0
0 1 0 0
0 0 0 1
1 0 0 0
Изход
Пътища: 3
Подсказка

Направи втора таблица ways със същия размер. В нея пиши по колко начина се стига до всяка клетка. До началото се стига по 1 начин. До стена по 0. До всяка друга клетка се стига или отгоре, или отляво, затова броят е сборът от клетката над нея и клетката вляво от нея (ако ги има). Попълни таблицата ред по ред, отгоре надолу. Отговорът е в последната клетка.

4. Най-краткият път. Трудна задача. Сега може да се движиш и в четирите посоки: нагоре, надолу, наляво и надясно. С колко стъпки най-малко се стига от горния ляв до долния десен ъгъл? Ако няма път, отпечатай „Няма път“.

Лабиринтът
0 0 0 0 0
1 1 1 1 0
0 0 0 0 0
0 1 1 1 1
0 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 свободни места едно до друго, и отпечатай номера на реда и на първото място.

Тест

Провери се

1. Какво създава new int[3, 5]?
2. Таблицата sales има 3 седмици (редове) и 5 дни (колони). Какво дава sales.GetLength(1)?
3. Коя е последната клетка на таблица с 3 реда и 5 колони?
4. Искаш сумата на една колона (един ден за всички седмици). Кой индекс се мени във вътрешния цикъл?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·