TrianglesContainOrigin

I have the problem description, examples and an input generator at http://web.eecs.utk.edu/~jplank/topcoder-writeups/2014/TrianglesContainOrigin/.

In this directory, I have my presentation in TrianglesContainOrigin.odp, converted to PDF in TrianglesContainOrigin.pdf.

Below is the commented code (src/make_jgraph.cpp) which creates the jgraph in the presentation, and also solves the problem in the fastest way:

/* TrianglesContainOrigin/src/make_jgraph.cpp
   James S. Plank
   September, 2020.

   This program takes input to the TrianglesContainOrigin Topcoder problem on 
   standard input, and then draws the jgraph which can help you visualize a solution.
   It then solves the problem and prints it as a comment on the last line in the 
   jgraph file.
 */

#include <string>
#include <vector>
#include <cmath>
#include <map>
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;

/* Bundling up all of my information in this data structure makes the problem easier. */

class Point {
  public:
    double x;          // The point's x value
    double y;          // The point's x value
    double angle;      // The point's angle from the origin (radians)
    int index;         // The point's number in the Points vector, which is sorted by angle.
};

int main()
{
  double hyp, a;                          // Hypotenuse, angle
  double pi;                              // Convenient to have pi in a variable
  map <double, Point *> m;                // Map of points sorted by angle
  vector <Point *> points;                // Vector of points sorted by angle
  vector <string> colors;                 // This is to draw the colored regions.
  long long num;                          // The number of triangles

  int i;                                  // Temporary variable
  int p1, p2, p3, p4;                     // These match the presentation
  double dx, dy;                          
  Point *p;
  map <double, Point *>::iterator mit;
  const char *c;

  /* Set pi, and put two colors (pink, blue) into colors */

  pi = acos(-1.0);
  colors.push_back("1 .7 .7");
  colors.push_back(".7 1 1");

  /* Read the points, calculate their angles from the origin and put them into the map */

  while (cin >> dx >> dy) {
    p = new Point;
    p->x = dx;
    p->y = dy;
    hyp = sqrt(dx*dx + dy*dy);
    p->angle = acos(dx/hyp);
    if (dy < 0) p->angle = 2 * pi - p->angle;
    m[p->angle] = p;
  }

  /* Now calculate their indices, and make the points array, sorted by angle. */

  for (mit = m.begin(); mit != m.end(); mit++) {
    p = mit->second;
    p->index = points.size();
    points.push_back(p);
  }

  /* Draw the jgraph axes */

  printf("newgraph\n");
  printf("xaxis min -10000 max 10000 size 6 nodraw\n");
  printf("yaxis min -10000 max 10000 size 6 nodraw\n");
  printf("clip\n");

  /* Draw the colored regions opposite each adjacent pair of points.
  If there are an odd number of points, make the first region light
  green, and alternate blue/pink for the others (I do this because
  otherwise the first and last regions are the same color).  */

  printf("\n(*Draw the colored regions *)\n\n");  // This is handy when you want to look
                                                   // at the jgraph.

  for (i = 0; i < (int) points.size(); i++) {
    p = points[i];   
    a = p->angle + pi;
    if (i == 0 && points.size()%2 == 1) {
      c = ".5 1 .5";
    } else {
      c = colors[i%colors.size()].c_str();
    }

    /* Multiplying by 100,000 means that points of the triangle will be outside of
       the axes.  By specifying "clip" above, anything outside of the axes won't
       be drawn. */

    printf("newline color %s poly pcfill %s pts %lg %lg 0 0 ", 
           c, c, 
           100000 * cos(a),
           100000 * sin(a));
    p = points[(i+1)%points.size()];   
    a = p->angle + pi;
    printf("%lg %lg\n", 100000 * cos(a), 100000 * sin(a));
  }

  printf("\n(*Draw the lines from the origin to each point *)\n\n");
  
  for (i = 0; i < (int) points.size(); i++) {
    printf("newline pts 0 0 %lg %lg\n", points[i]->x, points[i]->y);
  }

  printf("\n(*Draw the points *)\n\n");

  printf("newcurve marktype circle pts\n");
  for (i = 0; i < (int) points.size(); i++) {
    printf("  %lg %lg\n", points[i]->x, points[i]->y);
  }

  printf("\n(*Draw the origin *)\n\n");

  printf("newcurve marktype box color 1 0 0 pts 0 0\n");

  printf("\n(*Draw the labels if there are <= 26 points *)\n\n");

  if (points.size() <= 26) {
    for (i = 0; i < (int) points.size(); i++) {
      p = points[i];
      printf("newstring fontsize 14 hjc vjc x %lg y %lg : %c\n", 
             p->x + cos(p->angle)*500,
             p->y + sin(p->angle)*500,
             'A'+i);
    }
  }

  /* Do the calculation in the fastest way */

  /* Put a big ol sentinel onto the array. */

  p = new Point;
  p->x = 0;
  p->y = 0;
  p->angle = 4*pi; /* Actually, 3*pi will do */
  p->index = points.size();
  points.push_back(p);

  /* Now, enumerate p1 and p2, and incrementally calculate p3 and p4, and keep
     adding the number of points between p3 and p4 to the answer. */

  num = 0;
  p3 = 0;
  for (p1 = 0; p1 < (int) points.size()-1; p1++) {
    while (points[p3]->angle <= points[p1]->angle + pi) p3++;
    p4 = p3;
    for (p2 = p1+1; p2 < p3; p2++) {
      while (points[p4]->angle <= points[p2]->angle + pi) p4++;
      num += (points[p4]->index - points[p3]->index);
    }
  }
  
  printf("\n(*Number of triangles: %lld. *)\n", num);

  return 0;
}