Форум программистов, компьютерный форум CyberForum.ru

Ввод матрицы вручную - C++

Восстановить пароль Регистрация
 
 
Рейтинг: Рейтинг темы: голосов - 43, средняя оценка - 4.77
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
01.01.2011, 20:45     Ввод матрицы вручную #1
Возможно ли в данной программе сделать ввод матрицы ручным способом (или с помощью рандома, как-то удобнее будет) и введение пользователем координат начала пути и конца? Код пока для меня слишком сложный и сколько ни пытался - ни получилось...

Видимо, нужно добавить:

это и

C++
1
2
3
4
5
6
      {srand(time(0));
                
                cout<<" Labirint  "<<endl;
                for (int i=0; i<N; i++)
                        for (int j=0;j<M;j++)
                                Matr[i][j] = rand()%2;}
собственно, ввод

rowS
colS
rowE
colE


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include <iostream.h>
#include <iomanip.h>
#define    max_(a, b)  ((a)>(b)?(a):(b))
#define    min_(a, b)  ((a)<(b)?(a):(b))
const int  N =    8,         // число строк
           M =    8;         // и столбцов в матрице
const int  x =  1;         // численное значение клетки-"препятствия"
const int  E =    1,         // конечная
           S =   -1;         // и начальная точки
// исходная матрица: пустые клетки - нули, "препятствия" - x
// Конечная точка - E (coord: rowE,colE), исходная - S (rowS,colS)
int  Matr[N][M] = { 
    { x,  0,  0,  0,  x,  0,  E,  x },
    { 0,  0,  x,  0,  x,  0,  0,  0 },
    { 0,  x,  x,  0,  0,  0,  x,  0 },
    { 0,  0,  0,  x,  x,  x,  0,  0 },
    { 0,  0,  0,  0,  0,  0,  0,  0 },
    { 0,  x,  0,  x,  0,  x,  0,  0 },
    { 0,  0,  x,  0,  0,  0,  x,  0 },
    { x,  0,  S,  x,  0,  0,  0,  0 }
};
int    rowS  = 7,              // координаты начальной
       colS  = 2,    
       rowE  = 0,              // и конечной точки
       colE  = 6;
void   OutMatr();                                                 
void   OutWay(int matr[][M], int rS, int cS);
int    main()
{
          // копия исходной матрицы    
    int   Matrix[N][M];          
    int   i, j;
    for(i = 0; i < N; i++)
        for(j = 0; j < M; j++)
            Matrix[i][j] = Matr[i][j];
    int   n,
          Finish    = false,
          // координаты начала пути (если < 0 - пути нет)
          rS        = -1,      
          cS        = -1;
    
          // смещение
    int   offset[8] = { -1,0, 1,0, 0,-1, 0,1 };
    
          // координаты прямоугольника для поиска клеток со значением n
    int   startRow  = rowE,    
          endRow    = rowE,        
          startCol  = colE,
          endCol    = colE;
    // вывод исходного состояния матрицы
    OutMatr();                 
    // Цикл поиска клеток со значением n и заполнения соседних пустых (0) клеток
    // значением n + 1. Цикл завершается, когда нечего уже заполнять или достигнута
    // стартовая точка (со значением S)
    for(n = E; !Finish; n++)
    {
        Finish = true;
        for(i = startRow; i <= endRow; i++)
        {
            for(j = startCol; j <= endCol; j++)
            {
                if(Matrix[i][j] == n)
                {                      
                    for(int k = 0; k < 8; k += 2)
                    {
                        // проверяем значение клеток слева, справа, сверху и снизу
                        if( i + offset[k]     >= 0  && 
                            i + offset[k]     <  N  && 
                            j + offset[k + 1] >= 0  &&
                            j + offset[k + 1] <  M )
                        {
                            // и, если клетка существует и не занята (0), 
                            if(Matrix[i + offset[k]][j + offset[k + 1]] == 0)
                            {
                                // заносим n + 1
                                Matrix[i + offset[k]][j + offset[k + 1]] = n + 1;
                                Finish = false;
                            }
                            // если достигнута начальная точка (S) - запоминаем    координаты
                            // текущей точки(i, j), как начала пути(rS, cS)
                            else if(Matrix[i + offset[k]][j + offset[k + 1]] == S)
                            {
                                rS = i;
                                cS = j;
                                
                                // и выходим из всех циклов
                                goto Finish;
                            }
                        }
                    }  
                }
            }  
        }
        
        startRow   =   max_(0    , startRow - 1);
        endRow     =   min_(N - 1, endRow   + 1);
        startCol   =   max_(0    , startCol - 1);
        endCol     =   min_(M - 1, endCol   + 1);
    }  
Finish:
    if(rS >= 0)                                    // путь найден
    {
        OutWay(Matrix, rS, cS);
        OutMatr();
    }
    else
        cout << " \n\n\nbla - bla - bla \n\n";       // путь не найден, делай что хочешь
    cout << endl;
    return 0;
}
void OutMatr()
{
    static int k = 1;
    cout << (k-- ? "Source matrix:\n" : "\n\n\nOutput of the way:\n");
    for(int i = 0; i < N; i++)
    {
        for(int j = 0; j < M; j++)
            cout << setw(5) << Matr[i][j];
        cout << endl;
    }
}
// Вывод пути:
// от точки rS, cS - к ближайшей (со значением на 1 меньше),
// от нее-к следующей (на 1 меньше) и т.д.-до конечной (E)
void OutWay(int matr[][M], int rS, int cS)
{
    int   rW  = rS,
          cW  = cS;
    Matr[rW][cW] = -1;
    while(matr[rW][cW] != E)
    {
        if(rW > 0 && matr[rW - 1][cW] == matr[rW][cW] - 1)
            Matr[--rW][cW] = -1;
        else if(rW < M - 1 && matr[rW + 1][cW] == matr[rW][cW] - 1)
            Matr[++rW][cW] = -1;
        else if(cW < N - 1 && matr[rW][cW + 1] == matr[rW][cW] - 1)
            Matr[rW][++cW] = -1;
        else if(cW > 0 && matr[rW][cW - 1] == matr[rW][cW] - 1)
            Matr[rW][--cW] = -1;
    }
}
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
.::.DIMA.::.
142 / 142 / 4
Регистрация: 26.10.2008
Сообщений: 782
01.01.2011, 21:49     Ввод матрицы вручную #2
Возможно!
C++
1
2
3
for (int i = 0; i < N; i++)
    for (int  j = 0; j < M; j++)
        cin>>Matr[i][j];
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
01.01.2011, 22:57  [ТС]     Ввод матрицы вручную #3
Эм, ну ты кэпанул конечно Вопрос был в том, "куда" код ввода/рандомайза добавить, а не "какой" код.
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
02.01.2011, 00:33     Ввод матрицы вручную #4
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream.h>
#include <iomanip.h>
#define    max_(a, b)  ((a)>(b)?(a):(b))
#define    min_(a, b)  ((a)<(b)?(a):(b))
const int  N =    8,         // число строк
           M =    8;         // и столбцов в матрице
const int  x =  1;         // численное значение клетки-"препятствия"
const int  E =    1,         // конечная
           S =   -1;         // и начальная точки
// исходная матрица: пустые клетки - нули, "препятствия" - x
// Конечная точка - E (coord: rowE,colE), исходная - S (rowS,colS)
int  Matr[N][M];
int    rowS,              // координаты начальной
       colS,    
       rowE,              // и конечной точки
       colE;
void   OutMatr();                                                 
void   OutWay(int matr[][M], int rS, int cS);
int    main()
{
                srand(time(0));
                
                cout<<" Labirint  "<<endl;
                for (int i=0; i<N; i++)
                        for (int j=0;j<M;j++)
                                Matr[i][j] = rand()%2;
 
                cout << "Введите rowS: ";
                cin >> rowS;
                cout << "Введите colS: ";
                cin >> colS;
                cout << "Введите rowE: ";
                cin >> rowE;
                cout << "Введите colE: ";
                cin >> colE;
          // копия исходной матрицы    
    int   Matrix[N][M];          
    int   i, j;
    for(i = 0; i < N; i++)
        for(j = 0; j < M; j++)
            Matrix[i][j] = Matr[i][j];
// и продолжение твоего кода.................................
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
02.01.2011, 00:41  [ТС]     Ввод матрицы вручную #5
......
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
02.01.2011, 00:44     Ввод матрицы вручную #6
Что пробовать?! Выражайтесь яснее!!
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
02.01.2011, 00:59  [ТС]     Ввод матрицы вручную #7
Спокойнее не то написал.

Добавлено через 8 минут
Что-то я намудрил, теперь вообще путь не ищет =/
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
02.01.2011, 01:01     Ввод матрицы вручную #8
Напишите задание! Так быстрее будет понятно!
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
02.01.2011, 01:10  [ТС]     Ввод матрицы вручную #9
Да стандартная задача на выход из лабиринта, по коду не видно чтоли? В этом варианте прокладывает путь от заданной точки к другой заданной, на форуме есть подобная решенная задача, но там путь не "вычерчивается", а просто пишутся координаты, мне нужно либо к той задаче "приделать", чтобы она путь вычерчивала, либо к этой приделать векторы.

Вот ссыль Выход из лабиринта. В чем ошибка?

Добавлено через 3 минуты
Мне показалось, проще к той приделать вычерчивание пути, ан нет...
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
02.01.2011, 07:40     Ввод матрицы вручную #10
Позволил себе немного модернизировать код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <iostream>
#include <iomanip>
 
using namespace std;
 
#define MAX(a, b) (a > b ? a : b)
#define MIN(a, b) (a < b ? a : b)
 
const int N = 8,    // число строк и
          M = 8;    // столбцов в матрице
 
int Matr[N][M];     // исходная матрица
int rowS, colS,     // координаты начальной и
    rowE, colE;     // конечной точек
       
void OutMatr(int Matrix[N][M]);                                                 
 
void main()
{
    int Matrix[N][M] = {{0}};   // матрица с прохождением пути
 
    setlocale(LC_ALL, "Russian");
    srand(time(0));
 
    cout << "\t--- Лабиринт ---" << endl;
 
    for (int i = 0; i < N; i++)
        for (int j = 0; j < M; j++)
            Matr[i][j] = rand() % 2;
 
    OutMatr(Matr);  // вывод исходной матрицы
    
    cout << "Введите координаты начальной и конечной точек:" << endl
         << "Начальная точка расположена" << endl
         << "В строке №";   cin >> rowS;
    cout << "В столбце №";  cin >> colS;
    cout << "Конечная точка расположена" << endl
         << "В строке №";   cin >> rowE;
    cout << "В столбце №";  cin >> colE;
    
    int n = 2;  // пройденное расстояние
    int offset[8] = {-1, 0, 1, 0, 0, -1, 0, 1}; // смещение относительно клетки
    bool endSearch;
    int startRow = rowS,    // координаты прямоугольника для поиска клеток со значением n
        endRow   = rowS,
        startCol = colS,
        endCol   = colS;
 
    Matr[rowS][colS] = n;
    if (rowS == rowE && colS == colE) // Если начальная является конечной точкой
        goto Finish;
    do {        
        endSearch = false;
        for (int i = startRow; i <= endRow; i++)
            for (int j = startCol; j <= endCol; j++)
                if (Matr[i][j] == n)
                    for (int k = 0; k < 8; k += 2)
                        // если найдена конечная клетка
                        if (i + offset[k] == rowE && j + offset[k + 1] == colE) {
                            Matr[i + offset[k]][j + offset[k + 1]] = ++n;
                            goto Finish;
                        } else
                            // проверка что клетка не вышла за границы матрицы
                            // и что она пуста, т.е. равна 0
                            if (i + offset[k]     >= 0 && i + offset[k]     <  N &&
                                j + offset[k + 1] >= 0 && j + offset[k + 1] <  M &&
                                Matr[i + offset[k]][j + offset[k + 1]] == 0) {
                                    Matr[i + offset[k]][j + offset[k + 1]] = n + 1;
                                    endSearch = true;
                            }
        n++;
        startRow = MAX(0    , startRow - 1);
        endRow   = MIN(N - 1, endRow   + 1);
        startCol = MAX(0    , startCol - 1);
        endCol   = MIN(M - 1, endCol   + 1);
    } while (endSearch);
Finish:
    if (Matr[rowE][colE] != n)
        cout << "Путь не найден" << endl;
    else {
        Matrix[rowE][colE] = --n;
        for (int i = n; i > 1; i--)
            for (int k = 0; k < 8; k += 2)
                if (Matr[rowE + offset[k]][colE + offset[k + 1]] == n) {
                    Matrix[rowE + offset[k]][colE + offset[k + 1]] = --n;
                    rowE += offset[k];
                    colE += offset[k + 1];
                    break;
                }
        OutMatr(Matrix);
    }
    system("pause");
}
 
void OutMatr(int Matr[N][M])
{
    static int k = 1;
    cout << (k-- ? "Исходная матрица:\n" : "\nВывод путь:\n");
    for(int i = 0; i < N; i++) {
        for(int j = 0; j < M; j++)
            cout << setw(3) << Matr[i][j];
        cout << endl;
    }
}
Учтите, что строки и столбцы считаются начиная с 0!!!
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
02.01.2011, 07:46     Ввод матрицы вручную #11
СкринШот прилагается:
Миниатюры
Ввод матрицы вручную  
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
02.01.2011, 12:16  [ТС]     Ввод матрицы вручную #12
Спасибо, все работает, только вот ругается на

error C2065: 'time' : undeclared identifier
в строке с srand(time(0));

и соответственно, каждый раз один и тот же массив получается.

Добавлено через 37 секунд
и русская локаль почему-то не работает, пришлось транслитом писать...
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
02.01.2011, 15:25     Ввод матрицы вручную #13
А каким компилятором пользуешься?! Мной было написано на MS Visual C++ 2010 Express
Попробуй добавить библиотеки:
C++
1
2
#include <clocale>;
#include <cstdlib>;
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
02.01.2011, 20:12  [ТС]     Ввод матрицы вручную #14
А каким компилятором пользуешься?! Мной было написано на MS Visual C++ 2010 Express
MSVC++ 6.0

Тоже не работает...
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
02.01.2011, 20:15     Ввод матрицы вручную #15
А если для time добавить библиотеку еще
C++
1
#include <ctime>
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
02.01.2011, 20:19  [ТС]     Ввод матрицы вручную #16
Рандом заработал, иероглифы вместо русского осталось, да и черт с ними.
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
02.01.2011, 20:38     Ввод матрицы вручную #17
Вот посмотри разные варианты Русские шрифты
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
03.01.2011, 00:50  [ТС]     Ввод матрицы вручную #18
Можно ли как-то выдернуть отсюда алгоритм поиска выхода из лабиринта (и заодно вывод на экран пути в векторном виде, координаты)? То есть, чтобы по выбору было, задаешь начальную точку и 2 варианта - либо найти путь к конечной, либо просто найти выход (если есть).

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include<iostream>
#include <time.h>
#include<cmath>
using namespace std;
int main()
{
        setlocale(LC_ALL,".1251");
        srand(time(0));
              //создание динамического массива
        int N;
        cout<<"Vvedite razmer matricy(N*N)(N<=20)"<<endl;
        cin>>N;
        while((N<1)||(N>20))
                {cout<<" Razmer zadan neverno, vvedite zanovo "<<endl;
                cin>>N;}
        int **mas,elem;                 
        mas = new int *[N];         
        for (int i=0; i<N; i++)     
                 mas[i] = new int [N]; 
        int vybor;
        cout<<" Dlja zapolnenija labirinta sluchajno vvedite 1 "<<endl;
        cout<<"  Dlja zapolnenija labirinta vruchnuju, vvedite 2"<<endl;
        cin>>vybor;
        switch (vybor)
        {
        case 1:
                {srand(time(0));
                
                cout<<" Labirint  "<<endl;
                for (int i=0; i<N; i++)
                        for (int j=0;j<N;j++)
                                mas[i][j] = rand()%2;}
                break;
        case 2:
                {for(int i=0;i<N;i++)
                        for(int j=0;j<N;j++)
                                {cout<< "Vvedite jelement [ "<<i+1<<" ][ "<<j+1<<" ] (0-prohod,1-stena)"<<endl;
                                cin>>elem;
        while((elem!=0)&&(elem!=1))
                {cout<<" Nevernyj simvol. Vvedite 0 dlja prohoda, 1 dlja steny"<<endl;
                cin>>elem;}
        mas[i][j]=elem;}}break;
        default: cout<<"Nepravil'nyj vvod.Programma budet zavershena"<<endl;
        return 0;}      
        for ( i=0;i<N;i++)
                {for(int k=0;k<N;k++)
                        printf( "%3d",mas[i][k]);                       
                cout<<endl;}
         int k,l;
        cout<<" Vvedite koordinaty nachal'noj komnaty"<<endl;
        cin>>k>>l;
        while((k<0)||(l<0)||(k>=N)||(l>=N)||(mas[k][l]==1))
        {cout<<"Koordinaty zanjaty stenoj ili nahodjatsja vne labirinta. Vvedite koordinaty pravil'no "<<endl;
        cin>>k>>l;}     
//поиск пути
 
        mas[k][l]=-1;   
        for( int num=-1;num+N*N>0;num--)
                for(int i=1;i<N-1;i++)
                        for (int j=1;j<N-1;j++)
        if(mas[i][j]==num)
                        {{if(mas[i+1][j]==0)
                                mas[i+1][j]=num-1;}
                        {if(mas[i-1][j]==0)
                                mas[i-1][j]=num-1;}
                        {if(mas[i][j+1]==0)
                                mas[i][j+1]=num-1;}
                        {if(mas[i][j-1]==0)
                                mas[i][j-1]=num-1;}
                        }
                //нахождение максимума на границе
        int max=-N*N,mi=-1,mj=-1;
        for (  i=0;i<N;i++)
                {if((mas[i][0]>max)&&(mas[i][0]<0))
                        {max=mas[i][0];
                        mi=i;
                        mj=0;}
                if((mas[i][N-1]>max)&&(mas[i][N-1]<0))
                        {max=mas[i][N-1];
                        mi=i;
                        mj=N-1;}}
        for(int j=0;j<N;j++)
                {if ((mas[0][j]>max)&&(mas[0][j]<0))
                        {max=mas [0][j];
                        mi=0;
                        mj=j;}
                if ((mas[N-1][j]>max)&&(mas[N-1][j]<0))
                        {max=mas[N-1][j];
                        mi=N-1;
                        mj=j;}}
        //нахождение пути
 
        if(max==-N*N)
                cout<<"Prohoda net "<<endl;
        else 
        { cout<<"Iskomyj put"<<endl;
        for(int a=max-1;a<0;a++)
        {{if (mas[mi-1][mj]==a)
                mi=mi-1;}
        { if (mas[mi+1][mj]==a)
                mi++;}
        {if (mas[mi][mj-1]==a)
                mj--;}
        {if (mas[mi][mj+1]==a)
                mj++;}
        cout<<"[ "<<mi<<" ][ "<<mj<<" ]"<<endl;}}       
        return 0;
}
Добавлено через 5 минут
или нет, можете просто объяснить какая часть кода из поста №10 отвечает за вычерчивание пути вывод матрицы с вычерченным путем, тогда уж проще это проделать к проге в этом посте
Минич
 Аватар для Минич
66 / 66 / 3
Регистрация: 26.11.2010
Сообщений: 123
03.01.2011, 01:00     Ввод матрицы вручную #19
apachan, я Вас плохо понимаю! Чем будет отличаться конечная точка от выхода, вы же конечную точку вручную ставите?!
Цитата Сообщение от apachan Посмотреть сообщение
какая часть кода из поста №10 отвечает за вычерчивание пути
после 75 строки добавьте
C++
1
OutMatr(Matr);
и увидите что сначала происходит
а уже окончательный путь вырисовывается в блоке с 81 по 90 строки, включительно
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
03.01.2011, 01:10     Ввод матрицы вручную
Еще ссылки по теме:

C++ Ввод с клавы размера целочисленной матрицы, а затем самой матрицы. Найти наибольший элемент. Разрешается использовать только один оператор цикла
Ввод матрицы C++
C++ Ввод координат вручную

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
apachan
13 / 13 / 2
Регистрация: 07.12.2010
Сообщений: 127
03.01.2011, 01:10  [ТС]     Ввод матрицы вручную #20
apachan, я Вас плохо понимаю! Чем будет отличаться конечная точка от выхода, вы же конечную точку вручную ставите?!
Не, я хотел, чтобы у пользователя был выбор, либо он ставит конечную точку и программа ищет путь до нее, либо программа просто ищет выход из лабиринта

Добавлено через 5 минут
и кстати, программа из 10-го поста не учитывает, что если начальная или конечная точка равна "1" (стена то есть)
Yandex
Объявления
03.01.2011, 01:10     Ввод матрицы вручную
Ответ Создать тему
Опции темы

Текущее время: 15:18. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru