Advertisement
dmkozyrev

ConvexHull.cpp

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