БлогNot. Ещё 24 не пригодившихся первомайских задачки на C++

Ещё 24 не пригодившихся первомайских задачки на C++

Как-то нечего больше запостить, пусть будет хоть очередная коллекция, предыдущая - здесь.

Тематика задач разная - от типовых алгоритмов и обработки строк до число виндоузовских консольных заморочек, всё выполнялось в консоли Visual Studio 2015 (C++). Если Вы пришли на эту страницу из поисковика, для быстрого поиска на ней нужного слова нажмите в браузере комбинацию клавиш Ctrl+F и введите слово :)

1. Нарисовать синусоиду (или другой график функции одной переменной) в окне консоли.

Если пользоваться простыми методами WinAPI, то вот подход к решению:

#include <iostream>
#include <cmath>
#include <windows.h>
using namespace std;

double f(double x) {
 return sin(x);
}

int main() {
 HWND console_handle = GetConsoleWindow();
 HDC device_context = GetDC(console_handle);
 HPEN pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
 SelectObject(device_context, pen);
 
 int scale=100;
 bool first = true;
 for (double x=0; x<6.29; x+=0.01) {
  int cx = x*scale;
  int cy = floor(f(x)*scale);
  if (first) { 
   MoveToEx (device_context, cx, scale-cy, 0); 
   first = false;
  }
  else LineTo(device_context, cx, scale-cy);
 }

 ReleaseDC(console_handle, device_context);
 cin.ignore();
 return 0;
}

2. Вывести номер строки с наибольшим количеством букв "a" из файла.

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>

using namespace std;

int main() {
 ifstream f("data.txt");
 if (!f) {
  cout << "Can't open data.txt";
  cin.get(); return 1;
 }
 string line;
 char letter = 'a';
 int cnt = 0, lineNumber = 1, max = 0, maxLineNumber = 0;
 while (getline(f, line)) {
  cnt = count(line.begin(), line.end(), letter);
  if (cnt > max) {
   max = cnt; maxLineNumber = lineNumber;
  }
  lineNumber++;
 }
 f.close();
 cout << maxLineNumber;
 cin.get(); return 0;
}

Ну а если ответ может оказаться не единственным, проще всего, видимо, сканировать файл повторно, выводя все строки, в которых количество букв 'a' равно max.

3. Реализовать простой стек целых чисел на основе статического массива.

#include <iostream>
using namespace std;

#define MAX_SIZE 100 /* размер стека ограничен */

class Stack {
private:
 int A[MAX_SIZE];
 int top;
public:
 Stack() { top = -1; }

 int Push(int x) {
  if (top == MAX_SIZE - 1) {
   //cout << endl << "Stack is full!";
   return -1;
  }
  A[++top] = x;
  return 0;
 }

 int Pop() {
  if (top == -1) {
   //cout << endl << "Nothing to pop!";
   return -1;
  }
  top--;
  return 0;
 }

 int Top() {
  return A[top];
 }

 int IsEmpty() {
  if (top == -1) return 1;
  return 0;
 }

 void Print() {
  int i;
  cout << endl << "Stack: ";
  for (i = 0; i <= top; i++) cout << A[i] << " ";
 }
};

int main() {
 Stack S;
 S.Push(2);
 S.Push(5);
 S.Push(10);
 S.Print(); //2 5 10
 S.Pop();
 S.Print(); //2 5
 int A = S.Top();
 cout << endl << A; //5
 S.Push(12);
 S.Print(); //2 5 12
 cin.get();
 return 0;
}

4. Написать разложения в ряд Тейлора для функций экспоненты и синуса, выдающие результаты, до 14-15 знаков в дробной части совпадающие с результатами, возвращаемыми стандартными функциями exp и sin.

#include <iostream>
#include <cmath>
using namespace std;

long double _exp (long double x, long double eps) {
 long double ch = 1.;
 long double zn = 1.;
 long double res = 0.;
 long double res0 = 0.;
 for (unsigned int i=1; ; i++) {
  res += ch/zn;
  ch *= x;
  zn *= i;
  if (abs(res-res0)<eps) break;
  res0 = res;
 }
 return res;
}

long double _sin(long double x, long double eps) {
 long double ch = 1.;
 long double zn = 1.;
 long double res = 0.;
 long double res0 = 0.;
 int sign = 1;
 for (unsigned int i = 1; ; i += 2) {
  res += sign * ch / zn;
  ch *= x*x;
  zn *= (i+1)*(i+2);
  sign = -sign;
  if (abs(res - res0)<eps) break;
  res0 = res;
 }
 return res;
}

int main() {
 long double x = 1.;
 cout << "X=" << x << endl;
 long double eps = 1e-15;
 cout.precision(15);
 cout << "Eps=" << eps << endl;
 cout << "My exp(x)=" << _exp(x,eps) << endl;
 cout << "Standard exp(x)=" << exp(x) << endl;
 cout << "My sin(x)=" << _sin(x, eps) << endl;
 cout << "Standard sin(x)=" << sin(x) << endl;
 cin.get();
 return 0;
}

5. Написать программу, входными данными которой является возраст n человек (допустимо использовать случайные данные). Программа подсчитывает количество людей, возраст которых находится в интервале 10 лет, а именно:

  • 0—9 лет;
  • 10—19 лет;
  • 20—29 лет
  • и т.д.

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main() {
 const int limit = 100; //предельный возраст, необязательно кратен 10
 const int n = 15; //количество людей
 int decs = limit / 10 + 1; //сколько будет десятков лет
 int people[n];
 int *count = new int [decs];
 for (int i = 0; i<decs; i++) count[i] = 0;
 srand(time(0));
 for (int i=0; i<n; i++) {
  people[i] = rand()%limit;
  cout << people[i] << " ";
  count[people[i]/10]++;
 }
 cout << endl;
 for (int i = 0; i<decs; i++) cout << (i*10) << "-" << ((i+1)*10-1) << ": " << count[i] << endl;
 cin.get();
 return 0;
}

6. Выводить суммы элементов каждого столбца целочисленной матрицы до тех пор, пока не встретится столбец со всеми одинаковыми элементами.

#include <iostream>
using namespace std;
 
int main() {
 const int n = 4;
 const int m = 4;
 int a[n][m] = {
  { 1,2,3,4 },
  { 4,3,3,1 },
  { 5,1,3,2 },
  { 6,7,3,6 }
 };
 for (int j = 0; j < m; j++) {
  int sum = 0;
  bool different = false;
  for (int i = 0; i < n; i++) {
   sum += a[i][j];
   if (i > 0 && a[i][j] != a[i-1][j]) different = true;
  }
  if (!different) break;
  cout << "Column " << j+1 << ", Summa=" << sum << endl;
 }
 
 cin.get();
 return 0;
}

7. Написать рекурсивную функцию для вычисления суммы цифр произвольного целого числа, возможно, отрицательного. Выводимая сумма цифр всегда неотрицательна.

#include <iostream>
#include <cmath>

using namespace std;

int Sum(int n) {
 if (abs(n)<10) return abs(n);
 else return abs(n)%10 + Sum(abs(n) / 10) ;
}

int main() {
 int a = 123, b = 0, c = -147;
 cout << endl << Sum(a);
 cout << endl << Sum(b);
 cout << endl << Sum(c);
 cin.get();
 return 0;
}

8. Прочитать и сложить по 2 целых числа из текстового и бинарного файла. Использовать средства библиотеки cstdio.

Обычная проблема новичков - усвоение этих понятий :)

Если в файле действительно записаны двоичные числа (верхняя картинка), читаем их методом fread.

Если числа "записаны текстом", то есть, файл текстовый (нижняя картинка), можно использовать, например, fscanf.

Слева на картинках показаны в 16-ричном виде байты каждого файла, справа - как они выглядят "текстом":

бинарный и текстовый файл с 2 числами
бинарный и текстовый файл с 2 числами

Вот мы читаем и складываем 2 числа из первого (двоичного) файла:

//Visual Studio 2015
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
using namespace std;
 
int main(void) {
 FILE *fin = fopen("1.dat", "rb");
 int b,r=0;
 for (int k = 0; k < 2; k++) {
  fread(&b, sizeof(int), 1, fin);
  r+=b;
 }
 printf ("r=%d",r); //243
 fclose(fin);
 getchar(); 
 return 0;
}

Вот делаем то же самое со вторым (текстовым) файлом:

//Visual Studio 2015
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
using namespace std;
 
int main(void) {
 FILE *fin = fopen("1.txt", "r");
 int b,r=0;
 for (int k = 0; k < 2; k++) {
  fscanf (fin,"%d",&b);
  r+=b;
 }
 printf ("r=%d",r); //243
 fclose(fin);
 getchar(); 
 return 0;
}

Для простоты здесь не проверяется, существуют ли файлы, так что они должны находиться в текущей для программы папке, например:

текущая папка в решении с именем ConsoleVoid в Visual Studio
текущая папка в решении с именем ConsoleVoid в Visual Studio

Текстовый файл можно создать, например, в стандартном "Блокноте".

Двоичный - только в специализированном редакторе или написать отдельную программку, которая его создаёт, например, для наших чисел 121 и 122 это будет

//Visual Studio 2015
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
using namespace std;
 
int main(void) {
 FILE *fout = fopen("1.dat", "wb");
 int b=121;
 for (int k = 0; k < 2; k++) {
  fwrite(&b, sizeof(int), 1, fout);
  b++;
 }
 fclose(fout);
 getchar(); 
 return 0;
}

9. Заполнить матрицу числами по спирали. Элементы 0, 1, 2, ... размещаются при этом по возрастанию по спирали, направленной к центру по часовой стрелке начиная от верхнего левого угла матрицы, то есть

 0  1  2 3
11 12 13 4
10 15 14 5
 9  8  7 6

Размерность матрицы - любая. Короткий код даст рекурсия:

#include <iostream>
using namespace std;

int spiral(int m, int n, int j, int i) {
 return i ? m + spiral(n - 1, m, i - 1, m - j - 1) : j;
}

int main () {
 const int n = 5;
 const int m = 7;
 int a[n][m];
 for (int i = 0; i < n; i++) {
  for (int j = 0; j < m; j++) {
   a[i][j] = spiral(m, n, j, i);
   cout.width(4);
   cout << a[i][j] << " ";
  }
  cout << endl;
 }
 cin.get();
 return 0;
}

10. Дополнение предыдущей задачи. Отсортировать целочисленные элементы матрицы, располагая их по спирали.

То есть, если в предыдущей задаче мы должны были по индексам (i,j) элемента матрицы найти его место в спирали, здесь наоборот по порядковому номеру в спирали восстанавливаем индексы.

#include <iostream>
using namespace std;
 
void next_indeх(int k, int n, int m, int &i, int &j, int dn, int dm) {
 //i,j - индексы k-го по порядку элемента в матрице n*m при обходе 
 //по спирали к центру по часовой стрелке начиная от верхнего левого угла матрицы
 //(нумерация k с единицы)
 if (n < 1 || m < 1 || k<1 || k>n*m) { //ошибка
  i = j = -1; return;
 }
 if (k <= m) { //первая строка
  i = 0 + dn; j = k - 1 + dm; return;
 }
 if (k <= m + n - 1) { //последний столбец
  i = k - m + dn; j = m - 1 + dm; return;
 }
 if (k <= (m + n - 1 + m - 1)) { //последняя строка
  i = n - 1 + dn; j = m - 1 - (k - (m + n - 1)) + dm; return;
 }
 if (k <= (m + n - 1 + m - 1 + n - 2)) { //первый столбец
  i = n - 1 - (k - (m + n - 1 + m - 1)) + dn; j = 0 + dm; return;
 }
 next_indeх(k - (2 * n + 2 * m - 4), n - 2, m - 2, i, j, dn + 1, dm + 1);
}
 
int main() {
 const int n = 4;
 const int m = 5;
 int a[n][m] = {
  { 1,2,3,4,7 },
  { 4,3,3,1,9 },
  { 5,1,3,2,-2 },
  { 6,7,3,6,15 }
 };
 
 int size = n*m;
 for (int i = 0; i < size - 1; i++) {
  for (int j = i + 1; j < size; j++) {
   int i1, j1, i2, j2;
   next_indeх(i + 1, n, m, i1, j1, 0, 0);
   next_indeх(j + 1, n, m, i2, j2, 0, 0);
   if (a[i1][j1] > a[i2][j2]) {
    int temp = a[i1][j1];
    a[i1][j1] = a[i2][j2];
    a[i2][j2] = temp;
   }
  }
 }
 for (int i = 0; i < n; i++) {
  for (int j = 0; j < m; j++) {
   cout.width (5);
   cout << a[i][j];
  }
  cout << endl;
 }
 cin.get();
 return 0;
}

11. В одномерном массиве, вводимом с клавиатуры и состоящем из N вещественных элементов, вычислить сумму модулей элементов массива (использовать рекурсивную функцию для вычисления).

#include <iostream>
#include <cmath>
using namespace std;
 
double sumarray(double array[], int i) {
 if (i <= 0)  return 0;
 return sumarray(array, i-1) + abs(array[i-1]);
}
 
int main() {
 const int n = 10;
 double array[n] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, -10 };
 cout << "sum is : " << sumarray(array, n) << endl;
 cin.ignore(); return 0;
}

12. Пользователь вводит веса гирь в граммах (количество гирь заранее неизвестно) и вес, который нужно распределить (в граммах).

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

Если не осталось не распределённого веса, выводим сообщение, что распределение прошло успешно, иначе выводим сообщение об остатке.

Задача очень примитивна, так как не требует, например, минимального количества гирь для взвешивания. Для решения такой задачи подойдёт указанная ссылка, надо просто выбрать вектор или векторы минимальной длины из получившихся вариантов распределения суммы.

//Visual Studio 2015
#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
 
void error(char *message, int errorcode) {
 cout << endl << message;
 cout << endl << "Press Enter to EXIT";
 system ("pause>nul");
 exit (errorcode);
}
 
int main() {
 vector <int> weights; //Веса гирь в г
 int w; //один вес
 
 do {
  cout << "Type weight of bob or 0 to end: ";
  cin >> w;
  if (w<=0) break;
  weights.push_back(w);
 } while (1);
 int n = weights.size(); //количество гирь
 if (n < 2) error ("Needs 2 bobs at least!",1);
 
 cout << "Type weight of goods: ";
 cin >> w;
 if (w <= 0) error("Needs positive weight for goods!", 2);
  
 sort(weights.begin(), weights.end(), [](const int a, const int b) {return a > b; });
 
 if (weights[n-1] <= 0) error ("Needs positive weight for all bobs!",3);
  
  int i = 0; //номер гири
 do {
  int k = w / weights[i]; //количество гирь i-го вида
  w -= k*weights[i]; //остаток
  if (w < weights[n-1]) break;
  i++;
  if (i==n) i=0;
 } while (1);
 if (w) cout << "Remain of weight is " << w;
 else cout << "All goods are distributed";
 
 error ("OK",0);
 return 0;
}
веса гирь и товара
веса гирь и товара

13. Реализовать рекурсивный алгоритм построения цепочек из имеющегося набора костей домино.

Описываем доминошку как пару чисел, а лучше, как небольшой класс, с учетом того, что её придется переворачивать и выводить в консоль.

Делаем из имеющихся доминошек вектор, в цикле перебираем его элементы по правилу:

  • если можно добавить очередную доминошку в цепочку - добавить её, потом удалить из исходной цепочки и повторить процесс;
  • если доминошку добавить нельзя - смотрим, можно ли добавить перевёрнутую (6-1 вместо 1-6).

Имеем примерно такую программу для печати возможных цепочек, возможно, не оптимальную:

//Visual Studio 2015
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Domino {
public:
 int a,b;
 
 Domino(int a, int b) {
  this->a = a; this->b = b;
 }

 Domino flip () {
  Domino *dom = new Domino(b, a);
  return *dom;
 }

 string toString() {
  return "[" + to_string(a) + "/" + to_string(b) + "]";
 }
};

bool canAppend(Domino dom, vector <Domino> to) {
 return to.empty() || to[to.size()-1].b == dom.a;
}

void print(vector <Domino> chained) {
 for (int i = 0; i < chained.size(); i++)
  cout << chained.at(i).toString() << " ";
 cout << endl;
}

void dominoChains (vector <Domino> &chain, vector <Domino> vector) {
 if (vector.empty()) return;
 for (int i = 0; i < vector.size(); ++i) {
  Domino dom = vector.at(i);
  if (canAppend(dom, chain)) {
   chain.push_back(dom);
   print (chain);
   Domino saved = vector.at(i);
   vector.erase(vector.begin()+i);
   dominoChains(chain, vector);
   if (vector.empty()) return;
   vector.insert(vector.begin() + i, saved);
   chain.erase(chain.end() - 1);
  }
  else {
   dom = dom.flip();
   if (canAppend(dom, chain)) {
    chain.push_back(dom);
    print(chain);
    Domino saved = vector.at(i);
    vector.erase(vector.begin() + i);
    dominoChains(chain, vector);
    if (vector.empty()) return;
    vector.insert(vector.begin() + i, saved);
    chain.erase(chain.end() - 1);
   }
  }
 }
}

int main() {
 vector <Domino> list;
 Domino *d = new Domino(3, 4);
 list.push_back(*d);
 d = new Domino(5, 6);
 list.push_back(*d);
 d = new Domino(1, 4);
 list.push_back(*d);
 d = new Domino(1, 6);
 list.push_back(*d);
 d = new Domino(2, 2);
 list.push_back(*d);
 d = new Domino(2, 4);
 list.push_back(*d);

 vector <Domino> chained;
 dominoChains(chained, list);

 cin.get(); return 0;
}
задача о цепочках костей домино
задача о цепочках костей домино

14. Написать функцию, которая удаляет из строки символы, не являющиеся буквами или цифрами.

Всё-таки удобнее регулярных выражений для задач обработки строк ничего не придумано.

//Visual Studio 2015
#include <iostream>
#include <regex>
#include <string>
using namespace std;

void removeAllButLettersNumbers(string &text) {
 if (text.empty()) return;
 text = regex_replace(text, regex{ R"([^\d\w\s])" }, "");
     //если пробелы тоже удалять, то ([^\d\w])
}

int main() {
 string str ("Hello, hello-hello, I'm, crying \"OLOLO!\", folk?");
 removeAllButLettersNumbers(str);
 cout << str;
 cin.get(); return 0;
}

15. Обойти (или заполнить) по раскручивающейся спирали целочисленную матрицу. В принципе, подобная задача была на Паскале вот здесь.

Если не быть слишком требовательным к временной сложности алгоритма, можно решить так:

//Visual Studio 2015
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
 
void printItem (int a) {
 cout.width(3);
 cout << a << " ";
}
 
void spiral (int **arr, int X, int Y, int col_start, int row_start) {
 int x = col_start, y = row_start, dx = 1, dy = 0, step = 1;
 int filled = 0 , printable = X*Y;
 while (filled < printable) {
  for (int i =0 ; i < 2; i++) {
   for (int s = 0 ; s < step; s++) {
    if (x >= 0 && x < X && y >= 0 && y < Y) {
     //Здесь можно проверить, нет ли чего-то нужного в arr[y][x]
     printItem (arr[y][x]); 
     filled++;
    }
    x += dx; y += dy;
   }
   int t = dx; dx = -dy; dy = t;
  }
  step++;
 }
}
 
int main() {
 const int rows = 8;
 const int cols = 4;
 int **a = new int * [rows];
 int k = 1;
 for (int i = 0; i < rows; i++) {
  a[i] = new int [cols];
  cout << endl;
  for (int j = 0; j<cols; j++) {
   a[i][j] = k++;
   printItem (a[i][j]);
  }
 }
 
 cout << endl;
 int row_start = 2;
 int col_start = 3;
 spiral (a, cols, rows, col_start, row_start);
 
 cin.get(); return 0;
}
обход матрицы по раскручивающейся спирали с произвольной начальной точки
обход матрицы по раскручивающейся спирали с произвольной начальной точки

16. Найти 3 минимальных элемента целочисленного массива (не обязательно различных между собой).

Конечно, можно написать какой-то код для поиска хоть трёх, хоть пяти минимумов, но в реальности для таких вещей существуют сортировки. Приведённый код "портит" массив, подумайте, как сделать без этого : )

//Visual Studio 2015
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <climits>
using namespace std;
 
int main() {
 const int n = 10; //n чисел
 const int diap = 10; //в диапазоне значений от 1 до diap
 double a[n];
 srand(time(0));
 for (int i=0; i<n; i++) {
  a[i] = 1 + rand() % diap;
  cout << a[i] << " ";
 }
 
 int mins[3], idx;
 for (int j = 0; j < 3; j++) {
  bool flag = true;
  for (int i = 0; i < n; i++) {
   if (flag || a[i] < mins[j]) {
    mins[j] = a[i];
    idx = i;
    flag = false;
   }
  }
  a[idx] = INT_MAX; //убираем элемент из обработки для следующего шага
 }
 
 cout << endl << "Minimums= ";
 for (int j = 0; j < 3; j++) cout << mins[j] << " ";
 
 cin.get(); return 0;
}

17. Построить двумерную таблицу значений функции 2 переменных f(x,y) = ln(x+y/x-y), аргумент x меняется от a до b включительно с шагом dx, y меняется от c до d включительно с шагом dy. Если часть значений функции невозможно вычислить, печатать вместо них символы "*". Выводимые значения округлить до 2 знаков в дробной части.

//Visual Studio 2015
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
 
int main() {
 double a = 0, b = 2, c = 0, d = 2, dx = 0.2, dy = 0.2; //или заменить вводом величин с клавиатуры
 cout << setiosflags(ios::right) << setw(6) << "y|x";
 for (double x = a; x <= b; x += dx)
  cout << setiosflags(ios::right) << setw(6) << setprecision(2) << x; //заголовки x
 for (double y = c; y <= d; y += dy) {
  cout << endl << setiosflags(ios::right) << setw(6) << setprecision(2) << y;
  for (double x = a; x <= b; x += dx) {
   double t = x + y / x - y;
   if (x==0 || t<=0) { //чтобы не печатал inf и прочее, не try-catch
    cout << setiosflags(ios::right) << setw(6) << "*";
   }
   else {
    double f = log(t); //функция - как в условии
    cout << setiosflags(ios::right) << setw(6) << setprecision(2) << fixed << f;
   }
  }
 }
 
 cin.get(); return 0;
}
двумерная таблица значений f(x,y)
двумерная таблица значений f(x,y)

18. Написать функцию для сложения положительных чисел, представленных как строки string ("длинная арифметика", по сути, реализующая школьное сложение в столбик).

//Visual Studio 2015
#include <iostream>
#include <string>
using namespace std;

string Summa(string str1, string str2) {
 int n1 = str1.length(), n2 = str2.length();
 if (n1 > n2) {
  swap(str1, str2); //Делаем более длинную строку второй
  swap (n1,n2);
 }
 string str; //Строка для записи результата
             //Перевернём обе строки
 reverse(str1.begin(), str1.end());
 reverse(str2.begin(), str2.end());
 int carry = 0;
 for (int i = 0; i<n1; i++) { //Сложим в столбик
  int sum = ((str1[i] - '0') + (str2[i] - '0') + carry);
  str.push_back(sum % 10 + '0');
  carry = sum / 10;
 }
 for (int i = n1; i<n2; i++) { //Оставшиеся цифры из более длинного числа
  int sum = ((str2[i] - '0') + carry);
  str.push_back(sum % 10 + '0');
  carry = sum / 10;
 }
 if (carry) str.push_back(carry + '0'); //Остаточек
 reverse(str.begin(), str.end()); //Вернём строку результата в запись от начала к концу
 return str; //Всё
}

int main() {
 string str1 = "987654321";
 string str2 = "1234567890";
 cout << Summa(str1, str2);

 cin.get(); return 0;
}

19. Вывести все числа из текстового файла, отсортировав их по сумме цифр числа. Если суммы цифр одинаковы, отсортировать их между собой по возрастанию.

Пример, как реализовать sort более, чем с одним критерием и как простейшим способом искать в ifstream целые числа, игнорируя "мусор".

//Visual Studio 2015
#include <iostream>
#include <cstdlib>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <cmath>
using namespace std;

void error(char *message, int errorcode) { //Сообщение о статусе программы и выход
 cout << endl << message;
 cout << endl << "Press Enter to EXIT";
 system("pause>nul");
 exit(errorcode);
}

int sumOfDigits(int n) {
 n = abs(n); //Сумму цифр ищем от положительного числа
 int s = 0;
 do {
  s += n % 10;
  n /= 10;
 } while (n);
 return s;
}

int main() {
 ifstream f ("data.txt");
 if (!f) error ("Can't open file data.txt",1); //Не удалось открыть файл

 //Прочитать из файла всё, что можно интерпретировать как целые числа
 vector <int> v;
 string word;
 int value;
 while (f >> word) {
  try {
   value = stoi(word);
  }
  catch (exception& e) {
   continue;
  }
  v.push_back(value); //и сунуть это в вектор
 }

 if (v.size()==0) error("No numbers in data.txt", 1); //Не найдено чисел в файле

 sort(v.begin(), v.end(), [](int a, int b) {
  int sumA = sumOfDigits (a), sumB = sumOfDigits(b);
  return sumA == sumB ? a < b : sumA < sumB;
  //Если суммы цифр равны, сортируем по значению, иначе по сумме цифр
 });

 for (int i=0; i<v.size(); i++) cout << v[i] << " ";

 error ("OK",0);
}

Тестовый файл data.txt:

123 45 bad data

ooo 321 -222

42kaka 0

Выдача программы:

0 -222 42 123 321 45
OK
Press Enter to EXIT

Если исходный файл заведомо не "загрязнён" посторонними словами, то есть, какого-то такого типа

121 35

  45  -12 

0

можно сократить цикл чтения файла до

 //Прочитать из файла только целые числа
 vector <int> v;
 int value;
 while (f >> value) {
  v.push_back(value); //и сунуть это в вектор
 }

- только глупо как-то выглядит :)

20. Создать вектор, содержащий цифры из из строки string.

#include <iostream>
#include <vector>
#include <string>
using namespace std;
 
int main() {
 string str = "91231829182918219289123";
 vector <int> v;
 int len = str.length();
 for (int i = 0; i<len; i++) v.push_back(str[len - 1 - i] - '0');
 
 for (int i = 0; i<len; i++) cout << v[i] << " ";
 //цифры числа в обратном порядке
 
 cin.get(); return 0;
}

Если по каким-то причинам мы не хотим использовать vector, а нужен "обычный" ("неуправляемый") массив, то так:

#include <iostream>
#include <string>
using namespace std;
 
int main() {
 string str = "91231829182918219289123";
 int len = str.length();
 int *a = new int[len];
 for (int i = 0; i<len; i++) a[i] = str[len-1-i] - '0';
  
 for (int i = 0; i<len; i++) cout << a[i] << " ";
  //цифры числа в обратном порядке
 
 cin.get(); return 0;
}

Важно понимать, что в любой кодировке цифры 0, 1, ..., 9 закодированы подряд идущими числами (48, 49, ..., 57).

Чтобы из символа '0' (тип char, код 48) получить целое число 0 (тип int, код - двоичное число 0, занимающее sizeof(int) байт памяти) нужно отнять от кода символа код нуля '0' (или число 48).

Точно так же с другими цифрами:

#include <iostream>
using namespace std;
 
int main() {
 char s = '5';
 cout << endl << s; //5, но это не число 5, а символ '5'
 cout << endl << (int)s; //53, это код символа '5'
 int sn = s - '0';
 cout << endl << sn; //5, а вот это уже число 5
 
 cin.get(); return 0;
}

21. Из текстового файла переписать в другой текстовый файл все символы с заменой символа '0' на '1' и наоборот.

Обратите внимание на способ посимвольного чтения файла с помощью fstream, позволяющий избежать "лишних" или "потерянных" символов в конце файла.

#include <iostream>
#include <fstream>
using namespace std;

void error(char *message, int errorcode) { //Сообщение о статусе программы и выход
 cout << endl << message;
 cout << endl << "Press Enter to EXIT";
 system("pause>nul");
 exit(errorcode);
}

int main() {
 ifstream f1("input.txt");
 if (!f1) error("Can't open file input.txt", 1); //Не удалось открыть файл для чтения
 ofstream f2("output.txt");
 if (!f2) error("Can't open file output.txt", 2); //Не удалось открыть файл для записи
 char c;
 while (1) {
  f1.read(&c, 1);
  if (f1.eof()) break;
  if (c == '0') c = '1';
  else if (c == '1') c = '0';
  f2.write(&c,1);
  
 }
 f2.close();
 f1.close();
 error ("OK",0);
}

22. Составить программу, запрещающую ввод в консоли определённых символов (например, символов нижнего ряда буквенных клавиш) в процессе работы программы (без нажатия Enter).

В C++ нет захвата символов со стандартного ввода без ожидания нажатия клавиши.

Потому что терминалы бывают разные, а существование клавиатуры вообще не предполагается.

Конечно, в конкретных средах можно ставить "костыли", но это будет не стандарт.

Например, в Studio под Windows можно в run-time "вырезать" нежелательный ввод (тот же "нижний ряд клавиш") примерно так:

//Visual Studio 2015
#include <iostream>
#include <conio.h>
#include <windows.h>
using namespace std;
 
int main() {
 SetConsoleCP(1251);
 SetConsoleOutputCP(1251);
 char c;
 string deleted = "zxcvbnm,./ZXCVBNM<>?ячсмитьбюЯЧСМИТЬБЮ";
 cout << "Type anything or 0 to exit" << endl;
 while (1) { 
  c = _getch();
  if (deleted.find(c) != string::npos) {
   cin.clear(); continue;
  }
  else if (c=='0') break;
  cout << c;
 }
 
 return 0;
}

Ввод символа '0' - выход.

Повторимся, это не стандарт и зависит от ОС/компилятора.

23. Как скрыть текстовый курсор ввода в консоли Windows?

Это тоже не стандарт, но в Studio можно так:

//Visual Studio 2015
#include <iostream>
#include <windows.h>
using namespace std;

void hide_cursor() {
 void* handle = GetStdHandle(STD_OUTPUT_HANDLE);
 CONSOLE_CURSOR_INFO structCursorInfo;
 GetConsoleCursorInfo(handle, &structCursorInfo);
 structCursorInfo.bVisible = FALSE;
 SetConsoleCursorInfo(handle, &structCursorInfo);
}

int main() {
 hide_cursor();
 cin.get();
 return 0;
}

24. Среди N параллелограммов с известными сторонами и одной диагональю выделить ромбы, среди них определить ромб с наибольшей диагональю.

Если уже заданы стороны параллелограммов, например, в структуре parallelogram, проверяем, что стороны равны, это и будут ромбы. Значения известной диагонали в коде случайные, вторая диагональ находится по школьной формуле.

//Visual Studio 2015
#include <iostream>
#include <cmath>
using namespace std;

struct parallelogram {
 double a, b, diag1;
};

int is_rhombus(parallelogram p) {
 return p.a == p.b;
}

int main() {
 const int n = 3;
 double diag_max = 0;
 int imax = -1;
 parallelogram p[n] = {
  { 4,4,2.5 },
  { 5,4,3.5 },
  { 3,3,1.5 }
  //Данные, диагонали заполнены случайно
 };
 for (int i = 0; i < n; i++) {
  if (is_rhombus(p[i])) {
   double diag2 = sqrt(4 * p[i].a * p[i].a - p[i].diag1 * p[i].diag1); 
    //Школьная формула для 2-й диагонали ромба
   //Ищем и запоминаем максимум из диагоналей
   if (diag2 > diag_max) {
    diag_max = diag2; imax = i;
   }
   if (p[i].diag1 > diag_max) {
    diag_max = p[i].diag1; imax = i;
   }
  }
 }
 if (imax>-1) {
  cout << "Max diag=" << diag_max << ", rhombus number = " << imax;
 }
 else {
  cout << "Rhombus not found!";
 }

 cin.get();
 return 0;
}

03.05.2018, 13:26 [5058 просмотров]


теги: учебное список c++ алгоритм

К этой статье пока нет комментариев, Ваш будет первым