Cod sursa(job #73377)

Utilizator scapryConstantin Berzan scapry Data 18 iulie 2007 11:16:07
Problema Rubarba Scor 20
Compilator cpp Status done
Runda Arhiva de probleme Marime 6.51 kb
#include <cassert>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

#define printf(...) /*qt rocks*/;

enum { maxpoints = 100001 };

int points;
int point[maxpoints][2];
int sorted[maxpoints];

inline bool equal(double, double);

struct Point
{
	double ang, dist;
	int id;

	/**
	 * Smaller by angle or, if equal angle, smaller by dist
	 */
	bool operator<(const Point &p) const
	{
		if(equal(ang, p.ang))
			return dist < p.dist;
		else
			return ang < p.ang;
	}

} polar_point[maxpoints];
int base; //point with lowest x (and lowest y if tie)

int nhull;
int hull[maxpoints];

double ans;

inline double square(double val)
{
	return val * val;
}

inline double dist_squared(const double a[2], const double b[2])
{
	return square(a[0] - b[0]) + square(a[1] - b[1]);
}

inline double dist(int a, int b)
{
	assert(a >= 0 && a < points);
	assert(b >= 0 && b < points);

	double pa[2] = { point[a][0], point[a][1] },
	       pb[2] = { point[b][0], point[b][1] };

	return sqrt( dist_squared(pa, pb) );
}

inline double dist(int p, const double a[2])
{
	double pp[2] = { point[p][0], point[p][1] };

	return sqrt( dist_squared(pp, a) );
}

inline bool equal(double a, double b)
{
	static const double eps = 1e-7;

	return fabs(a - b) < eps;
}

inline double cross(const double v[2], const double w[2])
{
	printf("cross %lf,%lf   %lf,%lf   %lf\n", v[0], v[1], w[0], w[1], v[0] * w[1] - v[1] * w[0]);

	return v[0] * w[1] - v[1] * w[0];
}

inline double dot(const double v[2], const double w[2])
{
	return v[0] * w[0] + v[1] * w[1];
}

inline bool leftof(int a, int b, int p)
{
	double v[2] = { point[b][0] - point[a][0], point[b][1] - point[a][1] },
	       w[2] = { point[p][0] - point[a][0], point[p][1] - point[a][1] };

	return cross(v, w) > 0;
}

inline bool leftof(int p, const double l[2][2])
{
	//a, b, p -- l[0], l[1], p
	
	double v[2] = { l[1][0] - l[0][0], l[1][1] - l[0][1] },
	       w[2] = { point[p][0] - l[0][0], point[p][1] - l[0][1] };

	return cross(v, w) > 0;
}

inline double point2line(int p, const double l[2][2])
{
	double v[2] = { l[1][0] - l[0][0], l[1][1] - l[0][1] },
	       w[2] = { point[p][0] - l[0][0], point[p][1] - l[0][1] };
	double ratio;
	double pro[2]; //projection of p on l

	printf("point2line (%d) %d %d line (%lf %lf) - (%lf %lf)\n",
			p, point[p][0], point[p][1],
			l[0][0], l[0][1], l[1][0], l[1][1]);

	ratio = dot(v, w) / dist_squared(l[0], l[1]);

	pro[0] = l[0][0] + v[0] * ratio;
	pro[1] = l[0][1] + v[1] * ratio;

	printf("ratio %lf. projection (%lf %lf)\n", ratio, pro[0], pro[1]);

	return dist(p, pro);
}

void find_base()
{
	base = 0;
	for(int i = 1; i < points; i++)
	{
		if(point[i][0] < point[base][0])
			base = i;
		else if(point[i][0] == base && point[i][1] < point[base][1])
			base = i;
	}

	printf("base %d (%d %d)\n", base, point[base][0], point[base][1]);
}

void turn_polar()
{
	for(int i = 0; i < points; i++)
	{
		polar_point[i].dist = dist(base, i);
		polar_point[i].ang = atan2(point[i][1] - point[base][1], point[i][0] - point[base][0]);
		polar_point[i].id = i;
	}
}

void sort_points()
{
	int i;

	sort(polar_point, polar_point + points);

	printf("sorted points:\n");
	for(int i = 0; i < points; i++)
		printf("(id %d) %d %d (ang %lf dist %lf)\n",
				polar_point[i].id, point[ polar_point[i].id ][0], point[ polar_point[i].id ][1],
				polar_point[i].ang, polar_point[i].dist);
	printf("\n");

	for(i = 0; i < points; i++)
		sorted[i] = polar_point[i].id;
}

void calc_hull()
{
	int i;

	hull[0] = sorted[0];
	hull[1] = sorted[1];
	nhull = 2;

	for(i = 2; i < points; i++)
	{
		if(nhull < 2)
		{
			printf("too few points. adding %d without thinking\n", i);
			hull[nhull++] = sorted[i];
			continue;
		}

		printf("%d %d %d\n", hull[nhull - 2], hull[nhull - 1], sorted[i]);

		if(leftof(hull[nhull - 2], hull[nhull - 1], sorted[i]))
		{
			//advance to next triplet
			hull[nhull++] = sorted[i];

			printf("ok\n");
		}
		else //this includes coliniarity
		{
			nhull--;
			i--; //check this point again

			printf("removed a point\n");
		}
	}

	hull[nhull++] = base; //always a non-detected hull member.

	printf("hull (%d points):\n", nhull);
	assert(nhull >= 3);
	for(i = 0; i < nhull; i++)
		printf("(%d) %d %d\n", hull[i], point[ hull[i] ][0], point[ hull[i] ][1]);
	printf("\n");
}

/**
 * Check if using a rectangle with one side containing [a,b] renders a better result.
 */
void check_using(int a, int b)
{
	int i;
	double dleft = 0, dright = 0,
	       perp_dleft = 0, perp_dright = 0,
	       total, perp_total, prod, tmp;
	double line[2][2], perp_line[2][2];

	//the line between point a and b
	line[0][0] = point[a][0];
	line[0][1] = point[a][1];
	line[1][0] = point[b][0];
	line[1][1] = point[b][1];

	//the perpendicular line
	perp_line[0][0] = point[a][0];
	perp_line[0][1] = point[a][1];
	if(line[1][0] - line[0][0] == 0) //line is parallel to Ox
	{
		perp_line[1][0] = perp_line[0][0] + 1; //any
		perp_line[1][1] = perp_line[0][1]; //perpendicular to Ox
	}
	else
	{
		perp_line[1][0] = perp_line[0][0] + 1; //any
		perp_line[1][1] = perp_line[0][1]
				- (1.0 * (line[1][0] - line[0][0])) / (line[1][1] - line[0][1]);
	}

	printf("points %d %d. line (%lf %lf) - (%lf %lf) and (%lf %lf) - (%lf %lf)\n",
			a, b,
			line[0][0], line[0][1], line[1][0], line[1][1],
			perp_line[0][0], perp_line[0][1], perp_line[1][0], perp_line[1][1]);

	for(i = 0; i < nhull; i++)
	{
		tmp = point2line(hull[i], line);
		if(leftof(hull[i], line))
		{
			if(tmp > dleft)
				dleft = tmp;
		}
		else
		{
			if(tmp > dright)
				dright = tmp;
		}

		tmp = point2line(hull[i], perp_line);
		if(leftof(hull[i], perp_line))
		{
			if(tmp > perp_dleft)
				perp_dleft = tmp;
		}
		else
		{
			if(tmp > perp_dright)
				perp_dright = tmp;
		}
	}

	printf("dright %lf dleft %lf. perp_dright %lf perp_dleft %lf.\n",
			dright, dleft, perp_dright, perp_dleft);

	total = dright + dleft;
	perp_total = perp_dright + perp_dleft;

	printf("total %lf perp_total %lf\n", total, perp_total);

	prod = total * perp_total;

	printf("prod %lf\n", prod);

	if(prod < ans)
		ans = prod;

	printf("------------------------------------------\n\n");
}

void go()
{
	find_base();
	turn_polar();
	sort_points();
	calc_hull();

	assert(nhull >= 3);
	ans = 1e20; //inf
	for(int i = 0; i < nhull; i++)
		check_using(hull[i], hull[ (i + 1) % nhull ]);

	printf("final ans %lf\n", ans);
}

int main()
{
	int i;
	FILE *f = fopen("rubarba.in", "r");
	if(!f) return 1;

	fscanf(f, "%d", &points);
	for(i = 0; i < points; i++)
		fscanf(f, "%d%d", &point[i][0], &point[i][1]);

	fclose(f);
	f = fopen("rubarba.out", "w");
	if(!f) return 1;

	go();

	fprintf(f, "%.2lf\n", ans);
	fclose(f);
	return 0;
}