Advertisement
dmkozyrev

convex_hull.cpp

Jan 16th, 2016
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.69 KB | None | 0 0
  1. #include <iostream>
  2. #include <fstream>
  3. #include <vector>
  4. #include <time.h>
  5. #include <stdlib.h>
  6. #include <algorithm>
  7. #include <math.h>
  8.  
  9. using namespace std;
  10. typedef int TypeOfPoint; // Тип точки
  11. typedef pair<TypeOfPoint, TypeOfPoint> Point; // Точка
  12. typedef vector<Point> Points; // Вектор из точек
  13. typedef vector<Point*> ConvexHull; // Выпуклая оболочка (указатели на точки)
  14.  
  15. void add_to_ConvexHull(ConvexHull &, Point * p); // Добавление точки в выпуклую оболочку
  16. void create_ConvexHull(ConvexHull &, Points &); // Создание выпуклой оболочки из вектора точек
  17. void print_Points(const Points &); // Вывод вектора точек на экран
  18. void print_ConvexHull(const ConvexHull &); // Вывод выпуклой оболочки на экран
  19. void printf_ConvexHull(const ConvexHull &, const string &); // Вывод выпуклой оболочки в файл
  20.  
  21. int main(){
  22.     ifstream fin("in.txt"); // Открываем файл для чления
  23.     if (!fin) return 1;
  24.    
  25.     Points v; // Вектор, хранящий точки
  26.    
  27. //  Считываем точки из файла:
  28.     for(TypeOfPoint x, y; fin>>x>>y; v.push_back(Point(x, y)));
  29.    
  30.     ConvexHull hull; // Выпуклая оболочка
  31.     create_ConvexHull(hull, v); // Создаем выпуклую оболочку из вектора точек
  32.  
  33.     printf_ConvexHull(hull, "out1.txt"); // Выводим в файл
  34.     return 0;
  35. }
  36.  
  37. void create_ConvexHull(ConvexHull & hull, Points & v){
  38. //  Создание выпуклой оболочки из вектора точек на плоскости
  39.     for(int i = 0, n = v.size(); i < n; i++){
  40.         add_to_ConvexHull(hull, &v[i]);
  41.         //print_ConvexHull(hull); // Раскомментировать, если нужен вывод промежуточных результатов на экран
  42.     }
  43. }
  44.  
  45. void add_to_ConvexHull(ConvexHull & hull, Point* p){
  46. //  Добавление точки к уже существующей выпуклой оболочке
  47.  
  48. //  Используемые функции:
  49.     void add_to_zero(ConvexHull &, Point*); // Добавление к пустой оболочке (1)
  50.     void add_to_one(ConvexHull &, Point*); // Добавление к оболочке, состоящей из одной точки (2)
  51.     void add_to_two(ConvexHull &, Point*); // Добавление к оболочке, состоящей из двух точек (3)
  52.     void add_to_other(ConvexHull &, Point*); // Добавление к оболочке, состоящей из множества точек (4)
  53.    
  54. //  Выбираем случай, исходя из размера оболочки:
  55.     switch(hull.size()){
  56.         case 0:  add_to_zero(hull, p);  break; // Случай 1
  57.         case 1:  add_to_one(hull, p);   break; // Случай 2
  58.         case 2:  add_to_two(hull, p);   break; // Случай 3
  59.         default: add_to_other(hull, p); break; // Случай 4
  60.     }
  61. }
  62.  
  63. double det(const Point * a, const Point * b){
  64. //  Подсчет определителя, первой строкой которого идут координаты точки a, второй строкой - координаты точки b
  65.     return (a->first * b->second - b->first * a->second);
  66. }
  67.  
  68. double square_triangle(const Point * a, const Point * b, const Point * c){
  69. //  Подсчет ориентированной площади треугольника, состоящего из вершин a, b и c
  70.     return ((det(a,b)+det(b,c)+det(c,a))/2.0);
  71. }
  72.  
  73. void add_to_zero(ConvexHull & hull, Point * p){
  74. //  Добавление указателя на точку к пустой выпуклой оболочке
  75.     hull.push_back(p);
  76. }
  77.  
  78. void add_to_one(ConvexHull & hull, Point * p){
  79. //  Добавление указателя на точку к выпуклой оболочке, состоящей из одной точки
  80.  
  81. //  Сравниваем уже имеющуюся точку с новой точкой. Если не совпадают, то добавляем
  82.     if (*hull[0] != *p) hull.push_back(p);
  83. }
  84.  
  85. void add_to_two(ConvexHull & hull, Point * p){
  86. //  Добавление указателя на точку к выпуклой оболочке, состоящей из двух точек
  87.  
  88. //  Считаем ориентированную площадь треугольника, состоящего из точек в оболочке и новой точки
  89.     double sq = square_triangle(hull[0], hull[1], p);
  90.  
  91. //  Объявим используемую функцию для проверки принадлежности к отрезку:
  92.     bool in_interval(const Point *, const Point *, const Point *);
  93.    
  94. //  Если площадь равна нулю, то проверяем, лежит ли новая точка на отрезке, соединяющем две точки выпуклой оболочки
  95.     if (sq == 0 ){
  96. //      Объявим используемую функцию для вычисления расстояния между двумя точками:
  97.         double dist(const Point *, const Point *);
  98.        
  99.         if (!in_interval(hull[0], hull[1], p)) // Если новая точка не лежит на отрезке, но ориентированная площадь равна нулю
  100.             if (dist(hull[0], p) > dist(hull[1], p)) // Если расстояние от новой точки до нулевой больше, чем от новой до первой
  101.                 hull[1] = p; // То заменяем первую точку на новую
  102.             else // Иначе
  103.                 hull[0] = p; // Заменяем вторую точку на новую
  104.         return; // Выходим из функции
  105.     }
  106.            
  107. //  Добавляем обавляем точку в конец
  108.     hull.push_back(p);
  109.    
  110. //  Если значение ориентированной площади отрицательно, то меняем местами точку вначале и вконце.
  111. //  Это позволяет сохранить направление обхода против часовой стрелки.
  112.     if (sq < 0) swap(hull[0], hull[2]);
  113. }
  114.  
  115. void erase_ConvexHull(ConvexHull & hull, int a, int b){
  116. //  Вырезает из выпуклой оболочки точки с позиции a до позиции b включительно
  117.     int n = hull.size();
  118.  
  119. //  Объявим функцию, которая вычисляет целое значение переменной по модулю n:
  120.     void to_0_n(int &, int);
  121.    
  122.     to_0_n(a, n); // Вычисляем a по модулю n
  123.     to_0_n(b, n); // Вычисляем b по модулю n
  124.    
  125.     if (a > b) // Если левый конец отрезка больше правого, то выходим из функции
  126.         return;
  127.        
  128.     if (a == b) // Если начало совпадает с концом, то нам нужно вырезать только один элемент
  129.         hull.erase(hull.begin()+a);
  130.     else // Иначе вырезаем весь отрезок
  131.         hull.erase(hull.begin()+a, hull.begin()+b);
  132. }
  133.  
  134. void to_0_n(int & val, int n){
  135. //  Функция вычисляет значение val по модулю n
  136.     if (val >= n) val %= n;
  137.     if (val < 0) val = n - abs(val) % n;
  138. }
  139.  
  140. void inc_to_0_n(int & val, int n){
  141. //  Функция увеличивает значение val, а затем вычисляет его по модулю n
  142.     ++val;
  143.     to_0_n(val, n);
  144. }
  145.  
  146. void dec_to_0_n(int & val, int n){
  147. //  Функция уменьшает значение val, а затем вычисляет его по модулю n
  148.     --val;
  149.     to_0_n(val, n);
  150. }
  151.  
  152. void shift_left(ConvexHull & hull, int k){
  153. //  Сдвигает все элементы выпуклой оболочки на k позиций влево
  154.     reverse(hull.begin(), hull.begin()+k-1);
  155.     reverse(hull.begin()+k, hull.end());
  156.     reverse(hull.begin(),hull.end());
  157. }
  158.  
  159. void shift_right(ConvexHull & hull, int k){
  160. //  Сдвигает все элементы выпуклой оболочки на k позиций вправо
  161.     shift_left(hull, hull.size()-k+1);
  162. }
  163.  
  164. double dist(const Point * A, const Point * B){
  165. //  Вычисляет расстояние от точки A до точки B
  166.    
  167. //  Вычтем точку A из точки B:
  168.     Point C(B->first - A->first, B->second - A->second);
  169.    
  170. //  Возводим каждую компоненту в квадрат, суммируем и извлекаем корень:
  171.     return (sqrt(pow(C.first, 2)+pow(C.second, 2)));
  172. }
  173.  
  174. bool in_interval(const Point * A, const Point * B, const Point * C){
  175. //  Проверяет, принадлежит ли точка C отрезку AB
  176. //  Точка C точно не принадлежит этому отрезку, если сумма расстояний от его концов до точки C больше длины отрезка
  177.    
  178. //  Функция, вычисляющая длину отрезка:
  179.     double dist(const Point*, const Point*);
  180.  
  181. //  Вычисляем длины отрезков AC, BC и AB:
  182.     double AC = dist(A, C);
  183.     double BC = dist(B, C);
  184.     double AB = dist(A, B);
  185.    
  186. //  Проверяем условие принадлежности:
  187.     return (AC + BC == AB);
  188. }
  189.  
  190. void add_to_other(ConvexHull & hull, Point * p){
  191. //  Добавляет новую точку к выпуклой оболочке, состоящей больше, чем из двух точек
  192. //  cout << "adding "<< p->first << " " << p->second << " ..." << endl;
  193. //  print_ConvexHull(hull);
  194.     pair<int, int> interval[] = {pair<int, int>(-1, -1), pair<int, int>(-1, -1)};
  195.     int n = hull.size(), cur = 0;
  196.     for(int i = 0; i < n; i++){
  197. //      Вычислим индекс j точки, следующей сразу за текущей точкой:
  198.         int j = i + 1;
  199.         to_0_n(j, n);
  200.        
  201. //      Посчитаем ориентированную площадь треугольника, образованного точками hull[i], p, hull[j]
  202.         double sq = square_triangle(hull[i], p, hull[j]);
  203.         if (sq == 0 && in_interval(hull[i], hull[j], p)) // Если она равна нулю и точка лежит на отрезке между точками hull[i] и hull[j]
  204.             return; // То выходим из функции
  205.        
  206. //      С этого момента мы считаем, что отрезок [i, j] выпуклой оболочки необходимо модифицировать
  207.  
  208.         if (sq >= 0){ // Если значение площади не отрицательно
  209. //          Если еще не задан текущий отрезок, который нам нужно модифицировать в выпуклой оболочке
  210.             if (interval[cur].first == interval[cur].second)
  211.                 interval[cur] = pair<int, int>(i, j); // То задаем его  
  212.             else if (interval[cur].second == i) // Иначе, если конец текущего отрезка совпадает с началом полученного отрезка [i, j]
  213.                 interval[cur].second = j; // Концом текущего отрезка становится конец полученного (то есть к концу текущего отрезка прибавляется полученный)
  214.             else if (interval[cur].first == j) // Иначе, если начало текущего отрезка совпадает с концом полученного отрезка [i, j]
  215.                 interval[cur].first = i; // Началом текущего отрезка становится начало полученнго (то есть к началу текущего отрезка прибавляется полученный)
  216.             else // Иначе, полученный отрезок [i, j] становится вторым отрезком, который необходимо модифицировать, и сразу же текущим
  217.                 interval[++cur] = pair<int, int>(i, j);
  218.         }
  219.     }
  220.    
  221. //  На выходе мы имеем два отрезка, которые необходимо соединить в один, если совпадают конец второго и начало первого:
  222.     if (interval[1].second == interval[0].first)
  223.         interval[0].first = interval[1].first;
  224.        
  225. //  Теперь мы будем рассматривать один целый отрезок [begin, end]
  226.     int begin = interval[0].first;
  227.     int end = interval[0].second;
  228.    
  229. //  Вычисляем его длину по модулю n:
  230.     int len = (end - begin);
  231.     to_0_n(len, n);
  232.    
  233.     if (begin > end && end != 0) { // Если начало отрезка больше конца
  234.         shift_left(hull, end); // То сдвигаем все элементы оболочки влево на величину, равную концу отрезка
  235.         begin -= end;
  236.         end -= end;
  237.     }
  238.    
  239. //  Крайние точки мы должны оставить в покое, поэтому:
  240.     inc_to_0_n(begin, n); // Увеличиваем на единицу начало отрезка
  241.     dec_to_0_n(end, n); // Уменьшаем на единицу конец отрезка
  242.    
  243.     switch (len){ // В зависимости от длины отрезка
  244.         case 0: return; // Ничего не делаем, если длина отрезка равна 0
  245.         case 1: hull.emplace(hull.begin()+begin, p); break; // Вставляем новую точку, если длина отрезка равна 1
  246.         case 2: hull[begin] = p; break; // Заменяем точку, расположенную вначале отрезка, на новую точку
  247.         default:
  248.             hull[begin] = p; // Заменяем точку вначале отрезка на новую точку
  249.             inc_to_0_n(begin, n); // Увеличиваем начало отрезка
  250.             erase_ConvexHull(hull, begin, end); // Вырезаем все остальные точки из отрезка [begin, end]
  251.             break;
  252.     }
  253. }
  254.  
  255. void print_Points(const Points & v){
  256. //  Вывод на экран вектора из точек
  257.     for(int i = 0, n = v.size(); i < n; i++)
  258.         cout<<"("<<v[i].first<<";"<<v[i].second<<") ";
  259.     cout<<endl;
  260. }
  261.  
  262. void print_ConvexHull(const ConvexHull & hull){
  263. //  Вывод на экран выпуклой оболочки, состоящей из указателей на точки
  264.     for(int i = 0, n = hull.size(); i < n; i++)
  265.         cout<<"("<<hull[i]->first<<";"<<hull[i]->second<<") ";
  266.     cout<<endl;
  267. }
  268.  
  269. void printf_ConvexHull(const ConvexHull & hull, const string & filename){
  270. //  Вывод в файл выпуклой оболочки, состоящей из указателей на точки
  271.     ofstream fout(filename);
  272.     for(int i = 0, n = hull.size(); i < n; i++)
  273.         fout<<hull[i]->first<<" "<<hull[i]->second<<endl;
  274. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement