Опубликован: 02.02.2011 | Уровень: для всех | Доступ: свободно
Лекция 47:

Решение задач на использование алгоритмов обработки данных

< Лекция 46 || Лекция 47: 1234 || Лекция 48 >

Алгоритмы на графах

Многие прикладные задачи и задачи повышенной сложности легко сформулировать в терминах такой структуры данных как граф. Для ряда подобных задач хорошо изучены эффективные (полиномиальные) алгоритмы их решения.

Для хранения графа в программе можно применить различные методы. Самым простым является хранение матрицы смежности, с помощью которой легко проверить, существует ли в графе ребро, соединяющее вершину одну с вершиной с другой. Основной же ее недостаток заключается в том, что матрица смежности требует, чтобы объем памяти был достаточен для хранения N2 значений, даже если ребер в графе существенно меньше, чем N2. Это не позволяет построить алгоритм со временем порядка O(N) для графов, имеющих O(N) ребер.

Данного недостатка лишены такие способы хранения графа, как одномерный массив длины N списков или множеств вершин. В таком массиве каждый элемент соответствует одной из вершин и содержит список или множество вершин, смежных ей.

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

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

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

Графы широко используются в различных областях науки и техники для моделирования отношений между объектами. Объекты соответствуют вершинам графа, а ребра – отношениям между объектами.

Пример 2. Задача "Тетраэдр"

Дано треугольное поле в виде равностороннего треугольника. Оно разбито на одинаковые равносторонние треугольники со сторонами в М раз меньшими, чем сторона большого треугольника ( рис. 46.1).

Общий вид треугольного поля

Рис. 46.1. Общий вид треугольного поля

Маленькие треугольники пронумерованы подряд с верхнего ряда вниз по рядам, начиная с 0. Числами показаны номера треугольников. I -му треугольнику приписана пометка Pi.

Имеется также тетраэдр (правильная треугольная пирамида) с ребром, равным длине стороны маленького треугольника. Тетраэдр установлен на S -м треугольнике. Все грани тетраэдра пронумерованы следующим образом:

  1. основание тетраэдра;
  2. правая грань тетраэдра, если смотреть сверху тетраэдра в направлении стороны АВ перпендикулярно ей;
  3. левая грань тетраэдра, если смотреть сверху тетраэдра в направлении стороны АВ перпендикулярно ей;
  4. оставшаяся грань.

Например, при S=2 жирной линией выделено нижнее ребро третьей грани, а при S=3 жирной линией выделено нижнее ребро второй грани. J -я грань тетраэдра имеет пометку Rj.

Имеется возможность перекатывать тетраэдр через ребро, но при каждом перекатывании взимается штраф, равный квадрату разности между пометками совмещаемой грани тетраэдра и треугольника. Требуется перекатить тетраэдр с треугольника S на D с наименьшим суммарным штрафом (S\ne D).

Входные данные находятся в текстовом файле INPUT.TXT. Первая строка содержит целые числа S, D и М (M<=90). Каждая из следующих M2 строк содержит пометку соответствующего треугольника. В последней строке записаны пометки граней тетраэдра. Пометки (как граней, так и треугольников) – целые неотрицательные числа, не превосходящие 300. Числа в одной строке разделены пробелами.

В выходной файл OUTPUT.TXT должно быть записано одно число – минимально возможный штраф.

Пример.

Входные данные Выходные данные
0 4 3
         4
         3
         8
       100
         7
         3
         2
        49
         9
7 50 100 8
9446

Описание решения.

Перейдем к графу следующим образом: вершина – маленький треугольник. Ребро – наличие возможности перекатить тетраэдр через ребро из одного треугольника в другой. Тогда, например, поле, изображенное на рис. 46.2, превратится в граф на рис. 46.3.

Пример треугольного поля

Рис. 46.2. Пример треугольного поля
Начальное положение развертки тетраэдра

Рис. 46.3. Начальное положение развертки тетраэдра

На этом графе требуется найти путь минимальной стоимости из одной вершины в другую. Поскольку веса ребер в этом графе зависят от того, какой именно гранью тетраэдр придет на соответствующий треугольник, то воспользуемся поиском в ширину. Но прежде проясним процесс перекатывания тетраэдра. В соответствии с условиями задачи начальное положение развертки тетраэдра показано на рис. 46.4.

Перекатывание тетраэдра

Рис. 46.4. Перекатывание тетраэдра

Обозначим его 1u (в основании грань номер 1, повернутая вверх). Очевидно, что всего существует 8 возможных различных состояний тетраэдра: 1u, 2u, Зu, 4u, 1d, 2d, 3d, 4d. На рис. 46.5 и рис. 46.6 приведены соответствующие развертки:

Развертки перекатывания тетраэдра вверх

Рис. 46.5. Развертки перекатывания тетраэдра вверх
Развертки перекатывания тетраэдра вниз

Рис. 46.6. Развертки перекатывания тетраэдра вниз

Составим теперь таблицу, отображающую, в какое из состояний переходит тетраэдр при перекатывании его вниз, вверх, вправо, влево из текущего состояния.

Вниз Вверх Вправо Влево
1u 4d x 3d 2d
1d x 4u 2u 3u
2u 3d x 4d 1d
2d x 3u 1u 4u
3u 2d x 1d 4d
3d x 2u 4u 1u
4u 1d x 2d 3d
4d x 1u 3u 2u

Для удобства использования этой информации введем следующее кодирование:

x 1u 1d 2u 2d 3u 3d 4u 4d
0 1 2 3 4 5 6 7 8
Вниз Вверх Вправо Влево
1 2 3 4

Получаем двумерный массив Т (8 строк, 4 столбца), который описывает все возможные перекатывания тетраэдра.

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

Основная идея решения заключается в следующем:

  1. заносим в очередь стартовую позицию S ;
  2. пока очередь не пуста, берем из очереди очередную позицию, ставим в очередь позиции, в которые тетраэдр может попасть за одно перекатывание.

При установке в очередь очередной элемент включает номер вершины на графе, тип прихода (1u...4d), текущий штраф после перехода в эту вершину. Элемент не нужно ставить в очередь, если текущий штраф больше ранее запомненного для этой вершины графа.

Далее приведем код программы.

#include "stdafx.h"
#include <iostream>
#include <cmath>
using namespace std;
void InputData();
void OutResult();
void InitGraph();
void Put(long long v, long long tv, long long cv);
void Get(long long *v, long long *tv, long long *cv);
void PutAll(long long v, long long tv, long long cv);
long long SQR(long long a);
int MaxM = 10;
int Table[8][4]  =  {
          8, 0, 6, 4,
          0, 7, 3, 5,
          6, 0, 8, 2,
          0, 5, 1, 7,
          4, 0, 2, 8,
          0, 3, 7, 1,
          2, 0, 4, 6,
          0, 1, 5, 3
          };
int MaxQ = MaxM * MaxM * MaxM;
int *p, *cp, *Pw, **g, **Q;
long long *R;
long long i, S, D, M, j, a, TS, QBegin, QEnd, V, TV, CV, Last;
//V – номер вершины
//TV – тип вершины
//CV – текущее значение штрафа
int _tmain(int argc, _TCHAR* argv[]){
  p = new int[MaxM * MaxM];
  cp = new int[MaxM * MaxM];
  Pw = new int[MaxM * MaxM];
  for (i = 0; i < MaxM * MaxM; i++)
    p[i] = cp[i] = Pw[i] = 0;
  g = new int*[MaxM * MaxM];
  for (i = 0; i < MaxM * MaxM; i++ ){
    g[i] = new int[4];
    g[i][0] = g[i][1] = g[i][2] = g[i][3] = 0;
  }
  Q = new int*[MaxQ + 1];
  for (i = 0; i < MaxQ  + 1; i++ ){
    Q[i] = new int[4];
    Q[i][0] = Q[i][1] = Q[i][2] = Q[i][3] = Q[i][4] = 0;
  }
  R = new long long[5];
  R[0] = R[1] = R[2] = R[3] = R[4] = 0;
  InputData();
  InitGraph();
  QEnd = 0;
  QBegin = 1;
  Put(S,TS,0);
  while (QBegin <= QEnd){
    Get(&V,&TV,&CV);
    PutAll(V,TV,CV);
  }
  OutResult();
  system("pause");
  return 0;
}
//описание функции ввода исходных данных
void InputData(){
  FILE *f;
  f = fopen("input.txt","r");
  fscanf(f,"%d %d %d",&S,&D,&M);
  for ( i = 0; i < M * M; i++ )
    fscanf(f,"%d",p + i);
  for ( i = 1; i < 5; i++ )
    fscanf(f,"%d",R + i);
  fclose(f);
}
//описание функции вывода результата
void OutResult(){
  FILE *f;
  f = fopen("output.txt","w");
  fprintf(f,"%d",cp[D]);
  fclose(f);
}
//описание функции создания графа по исходным данным
void InitGraph(){
  Pw[0] = 1;
  g[0][1] = 2;
  for ( i = 1; i < M; i++)
    for ( j = i * i; j < (i + 1) * (i + 1) - 1; j++){
      g[j][++Pw[j]] = j + 1;
      g[j + 1][++Pw[j + 1]] = j;
    }
  a = 4;
  TS = 1;
  for ( i = 1; i < M - 1; i++){
    for ( j = i * i; j < (i + 1) * (i + 1); j += 2){
      g[j][++Pw[j]] = j + a;
      g[j + a][++Pw[j + a]] = j;
      if ( S == j ) TS = 2;
    }
    a += 2;
  }
  for ( i = 0; i < M * M; i++)
    cp[i] = INT_MAX;
}
//описание функции постановки в очередь одной вершины графа
void Put(long long v, long long tv, long long cv){
  QEnd++;
  Q[QEnd][1] = v;
  Q[QEnd][2] = tv;
  Q[QEnd][3] = cv;
  cp[v] = cv;
}
//описание функции взятия из очереди очередной вершины графа
void Get(long long *v, long long *tv, long long *cv){
  *v = Q[QBegin][1];
  *tv = Q[QBegin][2];
  *cv = Q[QBegin][3];
  QBegin++;
}
/*описание функции постановки в очередь всех вершин, смежных с текущей*/
void PutAll(long long v, long long tv, long long cv){
  long nv, ntv, ncv, Dir, Base;
  for ( i = 1 ; i <= Pw[v]; i++ ){
    nv = g[v][i];
    if ( nv == v + 1 )
      Dir = 2;
    else if ( nv == v - 1 )
      Dir = 3;
    else if ( nv > v )
      Dir = 0;
    else Dir = 1;
    ntv = Table[tv-1][Dir];
    Base = (ntv + 1) / 2;
    if ( Base > 0 ) {
      ncv =  cv + SQR(p[nv] - R[Base]);
      if ( ncv < cp[nv] )
        Put(nv,ntv,ncv);
    }
  }
}
//описание функции возведения в квадрат
long long SQR(long long a){
  return a*a;
}
Листинг .
< Лекция 46 || Лекция 47: 1234 || Лекция 48 >
Денис Курбатов
Денис Курбатов
Владислав Нагорный
Владислав Нагорный

Подскажите, пожалуйста, планируете ли вы возобновление программ высшего образования? Если да, есть ли какие-то примерные сроки?

Спасибо!