Матрицы, указатели и функции
Пусть объявлена статическая матрица const n=6, m=10; int M[n][m];. Этим самым мы объявили не один, а n константных указателей:
M[0] или &M[0][0] – адрес начала 0-й строки,
M[1] или &M[1][0] – адрес начала 1-й строки, и т. д.,
M[n-1] или &M[n-1][0] – адрес начала (n-1)-й строки.
Как и для одномерного массива, эти указатели менять нельзя. Поэтому для матрицы с фиксированными размерностями следующая перестановка двух строк с номерами n1 и n2 с помощью указателей недопустима:
float *q; int n1=2, n2=4; q=M[n1];
M[n1]=M[n2]; /* Ошибка! */ M[n2]=q; // Ошибка!
Если первое присваивание правильно, то второе и третье присваивания ошибочны, так как каждый из константных указателей (M[i] и M[j]) нельзя использовать слева в операторе присваивания. Позже будет показано, как такие перестановки строк сделать, используя вспомогательный массив указателей.
Преимуществом языка С++ по сравнению с другими языками является следующая удобная для практического использования возможность. Функцию обработки одномерного массива можно использовать при работе с частью матрицы. Пусть отлажена функция сортировки одномерного массива с таким, например, прототипом void Fun1 ( int *x, int k); где x — адрес одномерного массива, n — его размерность. Такую функцию можно использовать не только для работы с одномерным массивом. Можно передать в качестве фактического параметра адрес какого-нибудь элемента матрицы. Рассмотрим следующие фрагменты программ.
1) for (int i=0; i<n; i++) Fun1 (M[i], m, …);
—независимаясортировка всех m элементов каждой строки.Это же можно записать и так: for (int i=0; i<n; i++) Fun1 (&M[i][0], m, …); В качестве фактического параметра передаём адрес начала i-й строки матрицы и количество участвующих в сортировке элементов, то есть всю строку.
2) for (int i=0; i<n; i+=2) Fun1 (M[i], 2*m, …);
— сначала сортируется 0-я и 1-я строки матрицы вместе как один одномерный массив размерности 2*m. Затем аналогично сортируем 2-ю и 3-ю строки и т. д., и, наконец, (n-2)-ю и (n-1)-ю строки. При этом существенно используется то, что элементы статической матрицы в памяти располагаются непрерывно по строкам. После последнего элемента i-й строки следует первый элемент следующей (i+1)-й строки. Заметим, что этот цикл будет правильно работать, если в матрице чётное количество строк. В качестве упражнения внести изменения в предыдущий фрагмент, чтобы он работал и для нечётного n.
3) for (int i=0; i<n; i++) Fun1 (&M[i][m/2], m/2, …);
— в каждойстроке матрицы в сортировке участвуют последние m/2 элементов строки. Предполагается, что m — чётное. В качестве упражнения внести изменения в этот фрагмент таким образом, чтобы в случае, если m— нечётное, сортировались последние m/2+1 элементов строки. Например, если m=15, должны сортироваться последние 8 элементов.
Функцию, которая обрабатывает одномерный массив, можно использовать также для обработки всей матрицы. Рассмотрим тест.
Пусть описана функция: int MyFun2 (int *x, int k); в которой по некоторому алгоритму для одномерного массива x размерности k получаем и возвращаем некоторый параметр (например, сумму положительных чисел) в виде одного целого числа. Как эту функцию использовать для получения и (или) вывода этого же параметра для всей матрицы (одного целого числа), объявленной в начале параграфа? Выберите правильные варианты ответов:
1) int Res=MyFun2 (&A[0][0], n); cout<<Res;
2) int Res=MyFun2 (A[0], m); cout<<Res;
3) int Res=MyFun2 (A[0], m*n); cout<<Res;
4) cout<< MyFun2 (&A[0][0], n*m);
Решение. В первом варианте будет обработано 4 первых (из шести) числа первой с 0-м номером строки матрицы. Так как &A[0][0] и A[0] для матрицы — это адрес её начала, то есть адрес элемента A[0][0], то во втором варианте будут проанализированы m=6 элементов той же первой с 0-м номером строки матрицы. Правильными будут третий и четвёртый варианты, в которых найденный в функции параметр будет получен для m*n элементов матрицы, начиная с A[0][0], то есть с самого начала матрицы. Заметим, что так как второй параметр функции — это целое число, то m*n и n*m — это одно и то же значение, то есть общее количество элементов матрицы.
Так как элементы столбца располагаются в памяти не рядом друг с другом, как строки, а на определенном расстоянии, то для обработки столбца матрицы этот метод неприменим. Для этого можно, как это делается в языке Pascal и при обработке строк матрицы, скопировать столбец в одномерный массив и передать этот дополнительный массив в функцию в качестве фактического параметра. Если функция не просто получает параметр (MyFun2), а преобразовывает одномерный массив, например, сортирует его, то преобразованный с помощью функции одномерный массив надо обратно скопировать на место столбца матрицы. Напомним, что обработка матрицы по столбцам неэффективна, и её по возможности желательно избегать.
Рассмотрим, как передать всю матрицу в качестве параметра самостоятельной функции без использования класса. Здесь рассматривается этот вопрос без явного использования указателей.
Независимо от того, является ли матрица входной для функции, получается в ней или одновременно входной и выходной, т.е. передаётся в функцию, преобразуется в ней и возвращается в точку вызова, правила передачи двумерного массива в качестве параметра функции одинаковы.
Есть два варианта объявления матрицы в скобках в заголовке функции. Пусть объявлены две глобальные константы: const n=5, m=10;
Вариант 1. С помощью явных или предварительно объявленных глобальных констант в матрице, записываемой в качестве параметра функции, указываем обе размерности: void FunMatr11( int M[n][m], …);. В этом варианте обрабатываем все (у нас 5) строки. Можно через параметр (size1) передать меньшее их количество: void FunMatr12( int M[n][m], short size1, …);.
Во втором варианте первую левую размерность можно оставить свободной, записав пустые квадратные скобки. При этом количество столбцов обязательно указываем: void FunMatr21( int M[][m], int size1, …); В этом способе, как правило, передаётся в качестве параметра и количество строк (size1), которое используется в соответствующих циклах.
В обоих вариантах в функции необязательно обрабатывать указанное количество столбцов (у нас m=10). Его, как и первую размерность, можно также передать в качестве параметра (size2) и затем использовать в теле функции:
void FunMatr(int M[][m], int size1, int size2,…);.
Нельзя оставить свободными обе размерности или одну вторую правую размерность. В таком случае непонятно, где заканчивается предыдущая строка матрицы и начинается следующая. А при использовании двух индексов это важно.
В вызываемой функции (у нас в main) объявляем матрицу int A[n][m]; учитывая следующее ограничение: количество столбцов при объявлении должно быть точно таким, как и в заголовке функции. Количество строк может быть меньше, чем в заголовке, например, int B[3][m]: При вызове функции в любом случае указываем только имя матрицы без указания размерности и типа её элементов и, если надо, реальное количество строк и столбцов:
FunMatr11( A, …); FunMatr12( A, n, …);
FunMatr12( A, 3, …); FunMatr12( B, 3, …);
FunMatr12( B, 2, …); FunMatr2 ( A, 3, …);
FunMatr ( A, 3, 6, …);
Во всех вариантах n, 3, 2 — реальное количество обрабатываемых строк, 6 — реальное количество обрабатываемых столбцов.
Таким образом, при работе с матрицей надо уметь использовать следующие возможности функций.
1. Функция анализирует или получает одно число. Как такую функцию использовать для каждого элемента матрицы?
2. Параметром функции является одномерный массив, который в ней анализируется, получается или преобразуется. Как её использовать для каждой строки матрицы, для части строки, для нескольких соседних строк или для всей матрицы?
3. Функция работает со всей матрицей.
Пример 20. Составим функции для ввода(INP1), вывода матрицы (OUT1) и сортировки строк по элементам k –го столбца (SORT1), где k передаём как параметр функции. При сортировке строки переставляются местами, а порядок элементов каждой строки не меняется. Пример показывает, как передать всю матрицу в функцию.
const m2=3; /* Глобальная константа */
void INP1(int x[][m2] , int );
void OUT1(int x[][m2], int );
void SORT1(int x[][m2], int , int );
main()
{const n=5; int a[n][m2], k; INP1(a,n);
cout<<"\nOld matrix\n"; OUT1(a,n);
cout <<"\n The number of collumn ";
/* Ввод номера столбца с контролем ввода */
while(1) { cin>>k; if (k>=0 && k<m2) break;
cout<<"k>=0 && k<"<<m2<<" Repeat "; }
SORT1(a,n,k); cout<<"\nMatrix after sorting\n"; OUT1(a,n);
getch(); return 0; }
/* Для сортировки используется алгоритм выбора минимального элемента */
void SORT1(int x[][m2], int size1, int k)
{ int i,j,ind,mn,N;
for (ind=0;ind<size1-1;ind++)
{ mn=x[ind][k]; N=ind;
/* Поиск в k-м столбце минимального элемента (mn), начиная с элемента с номером ind, и его номера (N). */
for (i=ind+1;i<size1;i++)
if (x[i][k]<mn) { mn=x[i][k]; N=i; }
/* Перестановка N-й строки со строкой ind. */
for(j=0;j<m2;j++)
{ int t=x[ind][j]; x[ind][j]=x[N][j];
x[N][j]=t; } // The end of for (j …)
} // The end of for ( i…)
} // The end of funcion SORT1
void INP1(int x[][m2],int size1)
{ for (int i=0; i<size1; i++)
for (int j=0; j<m2; j++) x[i][j]=random(20)-10; }
void OUT1(int x[][m2], int size1)
{ int X,j,Y=wherey();
for (int i=0; i<size1; i++, Y++)
for (X=1, j=0; j<m2; j++, X+=7) { gotoxy(X,Y); cout<<x[i][j]; }
cout<<endl;
}
Пример 21. Составим следующие функции. Первая (InpMatr) вводит матрицу оценок по десятибалльной системе с контролем ввода. Если наберём при вводе число, меньше единицы или больше десяти, то программа даёт возможность повторить ввод этого же элемента матрицы без изменения индексов. Вторая функция MyOutCol выводит матрицу разным цветом. Строки, в которых есть хотя бы одна плохая оценка (1, 2 или 3) выделяем одним цветом, а остальные строки выводим другим цветом. Вспомогательная функция Inp1 с позиции с текстовыми координатами (x0, y0) вводит одну оценку с контролем ввода и используется в InpMatr для ввода каждого элемента матрицы. Функция Test возвращает true, еслив одномерном массиве есть хотя бы одна плохая оценка 1, 2 или 3, и false в противном случаe. Эта функция используется в MyOutCol для анализа каждой строки матрицы, а в main с её помощью анализируем всю матрицу. В функции MyOutCol два параметра цвета по умолчанию.
const m=5;
void Inp1(int x0, int y0, unsigned short &b)
{ /* Цикл для ввода одного числа с контролем ввода */
while (1) { gotoxy(x0,y0); cin>>b;
if (b>=1 && b<=10) return;
else { gotoxy(x0,y0); cout<<" "; }
} // The end of while
} // The end of funcion Inp1
void InpMatr (unsigned short a[][m], int );
bool Test(unsigned short *a1, int =m);
void MyOutCol (unsigned short a[][m], int , int =12, int=10);
int main() { const n=4; unsigned short A[n][m];
InpMatr(A, n); MyOutCol(A,n);
if (Test(A[0],n*m)) cout<<"\nYes"; else cout<<"\nNo";
getch(); return 0; }
void InpMatr (unsigned short a[][m], int size1)
{ int x, y, i, j;
for(y=wherey()+1, i=0; i< size1; i++, y++)
for(x=1, j=0; j<m; j++, x+=6) Inp1(x,y,a[i][j]); }
bool Test(unsigned short *a1, int size2)
{ for(int j=0; j<size2; j++) if (a1[j]<=3) return 1;
return 0; }
void MyOutCol
(unsigned short a[][m], int size1, int c1, int c2)
{ for (int i=0; i<size1; i++)
{ cout<<endl; /* Устанавливаем цвет для i – строки */
if (Test(a[i],m)) textcolor(c1); else textcolor(c2);
/* Вывод i – строки */
for(int j=0; j<m; j++)
cprintf("%5d", a[i][j]);
} // The end of for(i …)
} // The end of function MyOutCol
Пример 22. Ту же задачу (см. прим. 20) решим с помощью методов класса.
При работе со статической матрицей в методах класса матрица объявляется в классе в качестве поля с максимальными первой и второй размерностями, в качестве которых можно использовать глобальные константы (n1max и n2max). Реальное количество строк и столбцов также объявляем, как два поля класса (size1 и size2), и определяем их с помощью конструктора. Желательно в нём предусмотреть проверку, не превосходят ли реальные размерности матриц их наибольшие значения. В методах класса матрица и её размерности в качестве параметров не записываются. Значения элементов матрицы определяются одним из описанных в § 1 способов (вводятся с помощью метода INP1).
const unsigned n1max=5, n2max=10;
class MatrClass
{ double x[n1max][n2max]; unsigned size1, size2;
public:
MatrClass (unsigned n1, unsigned n2)
{ if (n1<=0 || n1>n1max) n1=n1max;
if (n2<=0 || n2>n2max) n2=n2max;
size1=n1; size2=n2; }
void INP1( ); void OUT1( ); void SORT1( int );
} ; // End of class
void MatrClass::INP1()
{ for (int i=0; i<size1; i++)
for (int j=0; j<size2; j++) x[i][j]=random(20)-10; }
void MatrClass::SORT1(int k)
{ int i,j,m,mn,N;
for (m=0;m<size1-1;m++) { mn=x[m][k]; N=m;
for (i=m+1; i<size1; i++)
if (x[i][k]<mn) { mn=x[i][k]; N=i; }
for(j=0;j<size2;j++)
{ double t=x[m][j]; x[m][j]=x[N][j]; x[N][j]=t; } }
}
void MatrClass::OUT1()
{ int X,j,Y=wherey();
for (int i=0; i<size1; i++, Y++)
for (X=1, j=0; j<size2; j++, X+=7) { gotoxy(X,Y); cout<<x[i][j]; }
cout<<endl; }
int main() { const N1=3, N2=6; int k;
MatrClass ObjMatr ( N1, N2);
ObjMatr.INP1();
cout<<"\nOld matrix\n"; ObjMatr.OUT1();
cout <<"\n Тhe number of collumn =>";
/* Ввод с контролем номера столбца, по которому будем сортировать */
while(1) { cin>>k; if (k>=0 && k<N2) break;
cout<<"k>=0 && k<"<<N2<<" Repeat "; }
ObjMatr.SORT1(k); cout<<"\n After sorting\n";
ObjMatr.OUT1();
getch(); return 0; }
Упражнения и тесты
1. Есть ли ошибки? Если есть, указать, в каких строках. Если нет, записать, что будет выведено.
const size2=6;
void Sort1(int X[],int m)
{ int min,Nmin;
for(int k=0;k<m-1;k++) { min=X[k]; Nmin=k;
for (int j=k;j<m;j++)
if(X[j]<min) {min=X[j]; Nmin=j; }
X[Nmin]=X[k]; X[k]=min; }
}
void Sortmatr2(int M[][size2],int n,int m)
{ for(int i=0;i<n;i+=2) Sort1( &M[i][0] ,m*2); }
void MyOut(int M[][size2],int n,int m)
{ for(int i=0;i<n;i++) { cout<<endl;
for(int j=0;j<m;j++)
printf("%4d ",M[i][j]);
} cout<<endl;
}
int main() { const n=4,m=6;
int A[n][size2]={{4, 3, -2}, {1, 11, -12, 13, 14},
{20, -21, 22, -23}, {30, 4, 3, 2,1} } ;
Sortmatr2(A,n,m); MyOut (A,n,m);
getch(); return 0;
}
2. Есть ли ошибки? Если есть, указать, в каких строках. Если нет, записать, что будет выведено.
const size2=6;
int Sum1(int *X, int m)
{ int S=0; for(int j=0; j<m; j++) S+=X[j];
return S; }
void SumM1 (int M[][size2],int n,int m, int *S1)
{ for(int i=0;i<n;i++) S1[i]=Sum1 ( &M[i][m/2] ,m/2); }
void SumM2 (int M[][size2],int n,int m, int *S2)
{ for(int i=0;i<n;i++) S2[i]= Sum1(&M[i][1] ,m-1); }
void MyOut(int *s, int n)
{ cout<<endl; for(int i=0;i<n;i++) cout<< s[i]<<" "; }
int main()
{ const n=4,m=6; int A[n][size2]={{4, 3, -2}, {1, 11, -12, 13, 14},
{20, -21, 22, -23}, {30, 4, 3, 2,1} },
S1[n] , S2[n];
SumM1 (A,n,m,S1); MyOut (S1,n);
SumM2 (A,n,m,S2); MyOut (S2,n);
getch(); return 0; }
3. Пусть описана функция:
int MyMax (int *x, int n)
{ int Max=x[0];
for int i=0; i<n; i++) if (x[i]>Max) Max=x[i];
return Max; }
3.a. Как её использовать для вывода наибольшего элемента каждой строки матрицы: const n=4, m=6; int A[n][m];? Выбери правильные варианты ответов:
1) cout<<MyMax (A[0], n*m);
2) for (int i=0; i<n; i++) cout<< MyMax (A[i], m)<< “ “;
3) for (int i=0; i<n; i++) printf(“%5d”, MyMax (A[i], m);
4) int Res=MyMax(&A[0][0], n*m); cout<<Res;
5) for (int j=0; j<m; j++) cout<< MyMax (A[j], n)<< “ “;
3.b. Как использовать ту же функцию MyMax для вывода наибольшего элемента всей матрицы const n=4,m=6; int A[n][m];?
Выбери правильные варианты ответов:
1) MyMax (A[0], n*m);
2) cout<< MyMax (&A[0], n*m);
3) cout<< MyMax (A[0][0], n*m);
4) int R=MyMax(&A[0][0], n*m); cout<<R;
5) int R=MyMax(&A[0][0], n); cout<<R;
6) int R; R=MyMax(A[0], m); cout<<R;
7) int R; R=MyMax(A[0], m*n); cout<<R;
8) cout<< MyMax(&A[0][0], n*m);
4. Пусть описана следующая функция:
void MyMax (int *x, int n, int &Max)
{ Max=x[0]; for (int j=1; j<n; j++)
if (x[j]>Max) Max=x[j]; }
Как её использовать для вывода наибольшего элемента всей матрицы
const n=4, m=6; int A[n][m];?
Выбери правильные варианты ответов:
1) int R=MyMax(&A[0][0], n*m); cout<<R;
2) int R; MyMax(&A[0], m*n, R); cout<<R;
3) int R; MyMax(A[0], n*m, R); cout<<R;
4) int R; cout<< MyMax(&A[0][0], n*m, R);
5) int R; MyMax(&A[0][0], m*n, R); cout<<R;
Задачи
1. Составить следующие функции для работы с матрицей: ввод матрицы; вывод; транспонирование матрицы с сохранением результата на новом месте; умножение исходной матрицы на транспонированную; головную функцию для проверки этих функций:
const int n2=3; void INP1(double x[][n2] , int );
void OUT1(double x[][n2], int );
void TRANS(double x[][n2], double y[][n2], int );
void MULT (double x[][n2], double y[][n2], double z[][n2], int );
int main() { const size=3; int k;
double a[size][n2], b[size][n2], c[size][n2];
INP1(a,size); cout<<"\nOld matrix\n"; OUT1(a,size);
TRANS(a,b,size); cout<<"\nTrans matrix \n"; OUT1(b,size);
MULT(a,b,c,size); cout<<"\nMultiplication \n"; OUT1(c,size);
getch(); return 0; }
void TRANS(double x[][n2], double y[][n2], int n)
{ for(int i=0; i<n; i++)
for(int j=0; j<n2; j++) y[i][j]=x[j][i]; }
void MULT (double x[][n2], double y[][n2], double z[][n2], int n)
{ int l, i, j, s;
for (i=0; i<n; i++)
for ( j=0; j<n2; j++) { s=0;
for (l=0; l<n2; l++) s+=x[i][l]*y[l][j];
z[i][j]=s; }
}
void OUT1(double x[][n2], int n)
{ int X,j,Y=wherey();
for (int i=0; i<n; i++, Y++)
for (X=1, j=0; j<n2; j++, X+=7) { gotoxy(X,Y); cout<<x[i][j]; }
cout<<endl; }
void INP1(double x[][n2],int n)
{ int Y=1, X; for (int i=0; i<n; i++)
{ Y++; X=-14;
for (int j=0; j<n2; j++)
{ X+=16; gotoxy(X,Y);
cout<<"a["<<i<<","<<j<<"]"<<" "; cin>>x[i][j]; }
}
}
2. Составить класс для работы с целочисленной матрицей, включив в него матрицу, конкретные текущие размерности, конструктор для ввода и проверки текущих размерностей и следующие методы: ввод матрицы; вывод матрицы; поиск наибольшего и наименьшего значений матрицы и номеров строк, где они находятся; перестановку двух строк матрицы. В функции main ввести размерности матрицы, создать объект и проверить составленные методы:
const n1max=10, n2max=5;
class ClMatr
{ int a[n1max][n2max], n1, n2;
public:
ClMatr(int size1=5, int size2=3)
{ if (size1>0 && size1<n1max) n1=size1; else n1=5;
if (size2>0 && size2<n2max) n2=size2; else n2=3; }
void MyInp(); void MyOut();
void MaxMin(int &, int &, int &, int &);
void Change(int, int);
}; // End of class
void ClMatr::MyInp()
{ int x, y=3;
for (int i=0; i<n1; i++)
{ y++; x=-14;
for (int j=0; j<n2; j++) { x+=16; gotoxy(x,y);
cout<<"a["<<i<<","<<j<<"]"<<" ";
cin>>a[i][j]; }
} };
void ClMatr::MyOut()
{ int x, y=wherey()+1, j;
for (int i=0; i<n1; i++, y++)
for (x=1, j=0; j<n2; j++, x+=7)
{ gotoxy(x,y);
cout<<a[i][j]; } cout<<endl;
}
void ClMatr::MaxMin (int &max, int &Nmax, int &min, int &Nmin)
{ max=min=a[0][0];
Nmax=Nmin=0;
for (int i=0;i<n1;i++)
for (int j=0;j<n2;j++)
if (max<a[i][j]) { max=a[i][j]; Nmax=i; }
else if (min>a[i][j]) { min=a[i][j]; Nmin=i; }
}
void ClMatr::Change(int k1, int k2)
{ if (k1!=k2)
for (int j=0; j<n2; j++) { int d=a[k1][j]; a[k1][j]=a[k2][j];
a[k2][j]=d; }
}
int main() { int n1, n2, Nmax, Nmin, max, min;
cout<<"\nn1="; cin>>n1; cout<<" n2="; cin>>n2;
ClMatr ob(n1,n2); ob.MyInp();
cout<<"\nOld matrix\n"; ob.MyOut();
ob.MaxMin(max,Nmax,min,Nmin);
cout<<"Max element= "<<max <<" in "<< Nmax << " row\n";
cout<<"Min element= "<<min <<" in "<< Nmin << " row\n";
if (Nmax!=Nmin) { ob.Change(Nmax,Nmin);
cout<<"Matrix after changing "; ob.MyOut(); }
else cout<<"Matrix is not changed \n";
getch(); return 0;
}
Требования к задачам.
1) Матрица имеет фиксированную размерность в виде констант.
2) В задачах 1— 9 уровня B и 1, 2, 5 уровня С разработать функцию для работы с одномерным массивом и использовать её для каждой строки матрицы.
3) Разработать несколько функций для работы со всей матрицей.
4) Разработать два варианта проекта: 1) разбить весь проект на самостоятельные функции; 2) функции включить в класс и с помощью конструктора создать и использовать объект.
Уровень B
Обработка строк матрицы.
1. В каждой строке матрицы найти указанные величины и из них построить один или два одномерных массива, размерности которых соответствуют количеству строк матрицы:
1) среднее значение среди положительных чисел и среднее значение среди отрицательных элементов;
2) второй наибольший элемент и его номер в строке. Если вторых наибольших элементов в строке несколько, найти номер первого из них;
3) сумму чисел до первого положительного числа, включая его;
4) есть ли нуль в строке? Для каждой строки получить 1, если есть, и 0 в противном случае;
5) первый отрицательный элемент и его номер. Если отрицательных чисел в строке нет, получить соответственно 0 и -1.
2. В матрице найти сумму наибольших элементов строк, наибольшую сумму элементов строк и номер такой строки (любой, если несколько строк имеют наибольшую сумму).
3. Найти наибольший среди наименьших элементов строк и наименьший среди наибольших элементов строк.
4. В матрице найти количество строк, у которых: 1) все нули; 2) есть хотя бы один нуль и номер первой такой строки.
5. Дана матрица A, в которой Ai,j — оценка i–го студента на j–м экзамене. Фамилии и названия предметов в памяти не хранятся:
1) найти количество отличников, т. е. количество строк, в которых только 9 и (или) 10. При выводе такие строки выделить другим цветом;
2) найти количество двоечников, т. е. количество строк, в которых есть хотя бы одна 1 и (или) 2 и (или) 3.
6. Умножить матрицу на вектор (одномерный массив).
7. Переставить строки, в которых находится наибольший и наименьший элементы матрицы.
Указание. На первом этапе необходимо найти номера строк (n1 и n2), в которых находятся наибольший и наименьший элементы всей матрицы. И если n1 != n2, то переставляем эти строки так, как показано в 3.5.
8. Из матрицы удалить все строки, состоящие из одних нулей.
Указание. В цикле проверяем каждую строку и если она содержит только нули, то удаляем её так, как показано в 3.5.
9. Каждую строку матрицы рассортировать по возрастанию.