Въведение и задача за свързаност

Николай Киров

1. Технология на провеждане на студентски състезания по програмиране
2. Техника на програмирането на състезания по програмиране
3. Пример за скелет на програма на С++, вход и изход:

#include <iostream>
using namespace std;
int main()
{
 {
  ifstream fin("data.inp"); // данните са в текущата директирия, файл data.inp
  ...
  fin >> k;     // пример за четене от файла
  ...
  while (fin >> k) { ... } // пример за организиране на цикъл за четене
  ...
 } // в края на блока се унищожава променливата fin и
   // деструкторът затваря файла data.inp
 ...
 {
 ofstream fout("data.out");  // отваряне на тесктов файл за писане
 ...
 fout << k; // пример за писане във файла data.out
 } // в края на блока се унищожава променливата fout и
   // деструкторът затваря файла data.out
}

4. Задача за свързаност (Робърт Седжуик, Алгоритми на С. Том 1, Глава 1 ).
Дадени са редица от двойки числа, където всяко число представлява някакъв обект и p-q означава, че "р е свързано с q". Нека връзката  "р е свързано с q" е транзитивна. Иска се да премахнем ненужните двойки от множеството, т.е. когато в програмата се въведе нова двойка р-q, тя трябва да се изведе само ако според досега въведените двойки p не е свързано с q.

Пример - входен файл:

3 4
4 9
8 0
2 3
5 6
2 9
5 9
7 3
4 8
5 6
0 2
6 1

Програма 1 - бързо намиране, стандартен вход и изход.
#include <iostream>
#define N 10000
using namespace std;
int main()
{
 int id[N];
 for (int i = 0; i < N; i++) id[i] = i;
 int p, q;
 while (cin >> p >> q)
 {
  if (id[p] != id[q])
  {
   int t = id[p];
   for (int i = 0; i < N; i++)
   if (id[i] == t) id[i] = id[q];
   cout << "   " << p << " " <<q << "\n";
  }
 }
}

Програма 2 - бързо намиране, вход от файл и изход  на файл.
#include <fstream>
#define N 10000
using namespace std;
int main()
{
 int id[N];
 for (int i = 0; i < N; i++) id[i] = i;
 int p, q;
 ifstream fin("ex1.inp");
 ofstream fout ("ex1.out");
 while (fin >> p >> q)
 {
  if (id[p] != id[q])
  {
   int t = id[p];
   for (int i = 0; i < N; i++)
   if (id[i] == t) id[i] = id[q];
   fout << p << " " << q << "\n";
  }
 }
}

Програма 3 - бързо обединение.
#include <iostream>
#define N 10000
using namespace std;
int main()
{
 int i, j, p, q, id[N];
 for (i = 0; i < N; i++) id[i] = i;
 while (cin >> p >> q)
 {
  for (i = p; i != id[i]; i = id[i]) ;
  for (j = q; j != id[j]; j = id[j]) ;
  if (i != j)
  {
   id[i] = j;
   cout << "   " << p << " " <<q << "\n";
  }
 }
}

Програма 3 - претеглено бързо обединение.
#include <iostream>
#define N 10000
using namespace std;
int main()
{
 int i, j, p, q, id[N], sz[N];
 for (i = 0; i < N; i++)
 {
  id[i] = i; sz[i] = 1;
 }
 while (cin >> p >> q)
 {
  for (i = p; i != id[i]; i = id[i]);
  for (j = q; j != id[j]; j = id[j]);
  if (i != j)
  {
   if (sz[i] < sz[j])
   {
    id[i] = j; sz[j] += sz[i];
   }
   else
   {
    id[j] = i; sz[i] += sz[j];
   }
   cout << "   " << p << " " <<q << "\n";
  }
 }
}

Програма 4 - бързо обединение с компресия на пътя чрез разполовяване.
#include <iostream>
#define N 10000
using namespace std;
int main()
{
 int i, j, p, q, id[N], sz[N];
 for (i = 0; i < N; i++)
 {
  id[i] = i; sz[i] = 1;
 }
 while (cin >> p >> q)
 {
  for (i = p; i != id[i]; i = id[i])
  {  int t = i; i = id[id[t]]; id[t] = i; }
  for (j = q; j != id[j]; j = id[j])
  {  int t = j; j = id[id[t]]; id[t] = j;  }
  if (i != j)
  {
   if (sz[i] < sz[j])
   {
    id[i] = j; sz[j] += sz[i];
   }
   else
   {
    id[j] = i; sz[i] += sz[j];
   }
   cout << "   " << p << " " <<q << "\n";
  }
 }
}