Министерство образования и науки Российской Федерации Новосибирский Государственный Технический Университет Кафедра прикладной математики Курсовая работа по практикуму на ЭВМ: структуры данных и алгоритмы Факультет: Группа: Студент: Преподаватель: прикладной математики и информатики Новосибирск 2009 1. Условие Проверить, является ли заданный граф транзитивным, т.е. для любых трёх вершин u, v и w выполняется условие: если u и w, а также v и w смежны, то вершины u и v также смежны. 2. Анализ задачи 2.1. Исходные данные задачи: n – количество вершин в графе, n N m – количество рёбер в графе, m M E – множество рёбер графа, E {(ui , vi ) | ui , vi N ,1 ui , vi n, i 1, m} 2.2. Результат: result {0;1} – если граф транзитивен, то result = 1, иначе 0 2.3. Решение: Неориентированный граф G (V , E ) является транзитивным, если u, v, w V (({u, v} E {v, w} E ) ({u, w} E )) . Иными словами, если в графе существуют ребро {u, w} и вершина v (не совпадающая с u и w), то рёбра {u, v} и {v, w} должны существовать или не существовать одновременно. Таким образом, чтобы проверить, является ли неориентированный граф транзитивным, достаточно для каждого его ребра {u, w} и вершины v, не совпадающей с u и w, проверить, что значение переключательной функции существования ребра {u, v} равно значению переключательной функции существования ребра {v, w} . Формальная постановка задачи Для каждого ребра {u, w} и вершины v данного графа, не совпадающей с u и w, проверить, что значение переключательной функции существования ребра {u, v} равно значению переключательной функции существования ребра {v, w} . При первом же найденном неравенстве ответ будет «граф не является транзитивным», в случае, если неравенств не обнаружено, ответ будет «граф является транзитивным». 3. Структуры данных, используемые для представления исходных данных и результатов задачи 3.1. Входные данные Внешнее представление неориентированного графа: <ребро-графа> ::= <вершина-графа> пробел <вершина-графа> <вершина-графа> ::= число Внутреннее представление неориентированного графа: матрица смежности (такое представление позволяет быстро определить наличие/отсутствие ребра в графе) int graph[n][n]; 3.2. Выходные данные Внешнее представление: текстовое сообщение «Graph is transitive» (граф транзитивен) или «Graph is not transitive» (граф не транзитивен) Внутреннее представление: bool is_transitive; // Транзитивен ли граф 4. Укрупнённый алгоритм решения задачи 4.1. { Граф = ВводГрафа ПроверкаНаТранзитивность(Граф) } 4.2. Алгоритм проверки на транзитивность { 2 for (i = 0.. N - 1) { for (j = i + 1..N - 1) { Если (есть ребро {i, j}) { for (k = 0..N – 1) { Если (i <> k И j <> k) { Если (СуществованиеРебра({i, k}) <> СуществованиеРебра({k, j})) { ОТВЕТ: Граф не транзитивен } } } } } } ОТВЕТ: Граф транзитивен } 5. Структура программы Текст программы разбит на два модуля. Модуль 1 содержит основную подпрограмму, осуществляющую ввод графа, проверку его на транзитивность и вывод текстового результата. Модуль 2 содержит подпрограмму проверки графа на транзитивность. 5.1. Состав модуля 1: Главная функция main: Назначение: ввод графа, вызов функции проверки, вывод результата Прототип: int main() 5.2. Состав модуля 2: Функция is_transitive: Назначение: проверка графа на транзитивность. Возвращает true в случае, если граф транзитивен и false в случае, если граф не транзитивен. Прототип: bool is_transitive(int **G, int N) Параметры: G – матрица смежности графа, N – количество вершин графа. 5.3. Структура программы по управлению: main is_transitive 6. Текст программы на языке C++ main.cpp #include <stdio.h> #include <stdlib.h> #include "transitive.h" int main() { char filename[16]; printf("Filename: "); gets(filename); FILE *fh = fopen(filename, "r"); if (!fh) { perror("Error opening file"); exit(EXIT_FAILURE); } int N, M; fscanf(fh, "%d", &N); fscanf(fh, "%d", &M); int **graph = new int *[N]; for (int i = 0; i < N; i++) { graph[i] = new int[N]; for (int j = 0; j < N; j++) { graph[i][j] = 0; } 3 } for (int i = 0; i < M; i++) { int a, b; fscanf(fh, "%d", &a); a--; fscanf(fh, "%d", &b); b--; graph[a][b] = graph[b][a] = 1; } if (is_transitive(graph, N)) { printf("Graph is transitive\n"); } else { printf("Graph is not transitive\n"); } exit(EXIT_SUCCESS); }} transitive.h bool is_transitive(int **G, int N); transitive.cpp #include <stdio.h> bool is_transitive(int **G, int N) { for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { if (G[i][j]) { for (int k = 0; k < N; k++) { if (i != k && j != k) { if ((bool)G[i][k] != (bool)G[k][j]) { return false; } } } } } } return true; } 7. Набор тестов Тест 1: Пустой граф Входные данные: 5 0 Выходные данные: Graph is transitive 1 2 3 4 5 Тест 2: Граф из двух вершин Входные данные: 2 1 1 2 Выходные данные: Graph is transitive 1 2 Тест 3: Изолированные вершины Входные данные: 5 3 2 3 3 4 4 2 Выходные данные: Graph is transitive 1 2 3 4 4 5 Тест 4: Полный граф Входные данные: 7 21 1 2 1 3 1 4 1 5 1 6 1 7 2 3 2 4 2 5 2 6 2 7 3 4 3 5 3 6 3 7 4 5 4 6 4 7 5 6 5 7 6 7 Выходные данные: Graph is transitive 2 3 1 4 7 5 Тест 5: Полный граф из теста 4 без одного ребра Входные данные: 7 20 1 2 1 3 1 4 1 5 1 6 1 7 2 3 2 4 2 5 2 6 2 7 3 4 3 5 3 6 3 7 4 5 4 6 4 7 5 7 6 7 Выходные данные: Graph is not transitive 5 6 2 1 3 4 5 7 6 Тест 6: Несколько компонент связности Входные данные: 6 6 1 3 1 6 3 6 2 4 4 5 5 2 Выходные данные: Graph is transitive 2 3 4 1 5 Тест 7: Добавили ребро к тесту 6 Входные данные: 6 7 1 3 1 6 3 6 2 4 4 5 5 2 2 6 Выходные данные: Graph is not transitive 6 2 3 4 1 5 6 6