- •1) Форд-Беллман
- •2) Флойд-Уоршелл
- •5) Алгоритм поиска компонент связности в графе
- •6) Поиск мостов
- •7) Поиск точек сочленения
- •8)Дейкстра
- •9) Эратосфен
- •10) Задача о назначениях
- •11) Алгоритм Куна нахождения наибольшего паросочетания в двудольном графе
- •12)Эйлер
- •13) Binpow
- •14) Расширенный Евклид
- •15)Длинка
- •16) Диофантовы уравнения
- •17) Нахождение степени делителя факториала
- •18) Дискретное логарифмирование
- •19) Нахождение площади простого многоугольника
- •20) Теорема Пика.
- •21) Пересечение окружности и прямой
- •22) Поиск общих касательных к двум окружностям
- •24) Префикс-функция. Алгоритм Кнута-Морриса-Пратта
- •25) Нахождение всех подпалиндромов
- •26) Максимальный подпалиндром
- •27) Нахождение наидлиннейшей возрастающей подпоследовательности
- •28) Динамика по профилю. Задача "паркет"
- •29) Нахождение наибольшей нулевой подматрицы
- •30) Гаусс
- •31) Интеграл по формуле Симпсона
- •32) Биномиальные коэффициенты
- •33) Числа Каталана
- •34) Ожерелья
- •35)Задача о ферзях
- •36)Ханойские башни
- •37) Правильные скобочные последовательности
- •38) Принцип включений-исключений
- •39) Теория Шпрага-Гранди. Ним
- •40) Оптимальный выбор заданий при известных временах завершения и длительностях выполнения
- •41) Игра Пятнашки: существование решения
- •42) Минимальное остовное дерево. Алгоритм Крускала
- •43) Количество всех остовов графа
- •44) Гамильтонов цикл
- •45) Задача о домино
- •46) Проверка на ацикличность и нахождение цикла
- •47) Топологическая сортировка
11) Алгоритм Куна нахождения наибольшего паросочетания в двудольном графе
Дан двудольный граф , содержащий вершин и рёбер. Требуется найти наибольшее паросочетание, т.е. выбрать как можно больше рёбер, чтобы ни одно выбранное ребро не имело общей вершины ни с каким другим выбранным ребром.
Теорема Бержа
Формулировка. Паросочетание является максимальным тогда и только тогда, когда не существует увеличивающих относительно него цепей.
int n, k;
vector < vector<int> > g;
vector<int> mt;
vector<char> used;
bool try_kuhn (int v) {
if (used[v]) return false;
used[v] = true;
for (size_t i=0; i<g[v].size(); ++i) {
int to = g[v][i];
if (mt[to] == -1 || try_kuhn (mt[to])) {
mt[to] = v;
return true;
}
}
return false;
}
int main() {
... чтение графа ...
mt.assign (k, -1);
vector<char> used1 (n);
for (int i=0; i<n; ++i)
for (size_t j=0; j<g[i].size(); ++j)
if (mt[g[i][j]] == -1) {
mt[g[i][j]] = i;
used1[i] = true;
break;
}
for (int i=0; i<n; ++i) {
if (used1[i]) continue;
used.assign (n, false);
try_kuhn (i);
}
for (int i=0; i<k; ++i)
if (mt[i] != -1)
printf ("%d %d\n", mt[i]+1, i+1);
}
12)Эйлер
int phi (int n) {
int result = n;
for (int i=2; i*i<=n; ++i)
if (n % i == 0) {
while (n % i == 0)
n /= i;
result -= result / i;
}
if (n > 1)
result -= result / n;
return result;
}
13) Binpow
int binpow (int a, int n) {
int res = 1;
while (n) {
if (n & 1)
res *= a;
a *= a;
n >>= 1;
}
return res;
}
14) Расширенный Евклид
В
то время как "обычный"
алгоритм Евклида просто находит
наибольший общий делитель двух
чисел
и
,
расширенный алгоритм Евклида находит
помимо НОД также коэффициенты
и
такие,
что:
Т.е. он находит коэффициенты, с помощью которых НОД двух чисел выражается через сами эти числа.
int gcd (int a, int b, int & x, int & y) {
if (a == 0) {
x = 0; y = 1;
return b;
}
int x1, y1;
int d = gcd (b%a, a, x1, y1);
x = y1 - (b / a) * x1;
y = x1;
return d;
}
15)Длинка
Структура данных
Хранить
длинные числа будем в виде вектора
чисел
,
где каждый элемент — это одна цифра
числа.
typedef vector<int> lnum;
Для
повышения эффективности будем работать
в системе по основанию миллиард, т.е.
каждый элемент вектора
содержит
не одну, а сразу
цифр:
const int base = 1000*1000*1000;
Цифры будут храниться в векторе в таком порядке, что сначала идут наименее значимые цифры (т.е. единицы, десятки, сотни, и т.д.).
Кроме того, все операции будут реализованы таким образом, что после выполнения любой из них лидирующие нули (т.е. лишние нули в начале числа) отсутствуют (разумеется, в предположении, что перед каждой операцией лидирующие нули также отсутствуют). Следует отметить, что в представленной реализации для числа ноль корректно поддерживаются сразу два представления: пустой вектор цифр, и вектор цифр, содержащий единственный элемент — ноль.
А)Вывод
printf ("%d", a.empty() ? 0 : a.back());
for (int i=(int)a.size()-2; i>=0; --i)
printf ("%09d", a[i]);
Б) Чтение
for (int i=(int)s.length(); i>0; i-=9)
if (i < 9)
a.push_back (atoi (s.substr (0, i).c_str()));
else
a.push_back (atoi (s.substr (i-9, 9).c_str()));
В) Сложение
int carry = 0;
for (size_t i=0; i<max(a.size(),b.size()) || carry; ++i) {
if (i == a.size())
a.push_back (0);
a[i] += carry + (i < b.size() ? b[i] : 0);
carry = a[i] >= base;
if (carry) a[i] -= base;
}
Г) Вычитание
int carry = 0;
for (size_t i=0; i<b.size() || carry; ++i) {
a[i] -= carry + (i < b.size() ? b[i] : 0);
carry = a[i] < 0;
if (carry) a[i] += base;
}
while (a.size() > 1 && a.back() == 0)
a.pop_back();
Д) Умножение длинного на короткое
int carry = 0;
for (size_t i=0; i<a.size() || carry; ++i) {
if (i == a.size())
a.push_back (0);
long long cur = carry + a[i] * 1ll * b;
a[i] = int (cur % base);
carry = int (cur / base);
}
while (a.size() > 1 && a.back() == 0)
a.pop_back();
Е) Умножение двух длинных чисел
lnum c (a.size()+b.size());
for (size_t i=0; i<a.size(); ++i)
for (int j=0, carry=0; j<(int)b.size() || carry; ++j) {
long long cur = c[i+j] + a[i] * 1ll * (j < (int)b.size() ? b[j] : 0) + carry;
c[i+j] = int (cur % base);
carry = int (cur / base);
}
while (c.size() > 1 && c.back() == 0)
c.pop_back();
Ж) Деление длинного на короткое
int carry = 0;
for (int i=(int)a.size()-1; i>=0; --i) {
long long cur = a[i] + carry * 1ll * base;
a[i] = int (cur / b);
carry = int (cur % b);
}
while (a.size() > 1 && a.back() == 0)
a.pop_back();
carry – остаток
