Problem:
Reflection Warp Machine
Problem
とある宇宙では、2次元の格子点上に
n
個の星が存在し、宇宙人達はReflection Warp Machineを使い、星間を移動している。
この装置は、任意の位置、角度で直線を引くことができる。
この直線を対称軸として、現在いる座標から線対称の座標に移動することができる。
ただし、星が存在しない座標に移動することはできない。
一度引いた直線は何度でも利用できる。
現在、(
x
0
,
y
0
)の星にいるある宇宙人はすべての星を訪れたいと考えている。
星は好きな順番に訪れることができる。
すべての星を訪れるのに最小で何本の直線を引く必要があるか求めよ。
Input
n
x
0
y
0
...
x
n−1
y
n−1
入力は全て整数で与えられる。
1行目に
n
が与えられる。
2行目以降
n
行に
i
番目の星の座標(
x
i
,
y
i
)が空白区切りで与えられる。
Constraints
2 ≤
n
≤ 8
−100 ≤
x
i
,
y
i
≤ 100
(
x
i
,
y
i
) ≠ (
x
j
,
y
j
) (
i
≠
j
)
Output
すべての星を訪れるために必要な最小の直線の数を1行に出力せよ。
Sample Input 1
3
0 0
0 1
1 0
Sample Output 1
2
Sample Input 2
4
0 0
0 1
0 2
0 3
Sample Output 2
2

Submitted solutions:

code_snippet: /*  -*- coding: utf-8 -*-
 *
 * 1581.cc: Reflection Warp Machine
 */

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<deque>
#include<algorithm>
#include<numeric>
#include<utility>
#include<complex>
#include<functional>
 
using namespace std;

/* constant */

const int MAX_N = 8;
const int MAX_M = MAX_N * (MAX_N - 1) / 2;

/* typedef */

// typedefs

typedef vector<int> vi;
typedef pair<int,int> pii;
typedef vector<pii> vpii;

template <typename T>
struct Pt {
  T x, y;

  Pt() {}
  Pt(T _x, T _y) : x(_x), y(_y) {}
  Pt(const Pt<T> &pt) : x(pt.x), y(pt.y) {}

  bool operator==(const Pt<T> pt) const { return x == pt.x && y == pt.y; }
  Pt<T> operator+(const Pt<T> pt) const { return Pt<T>(x + pt.x, y + pt.y); }
  Pt<T> operator-() const { return Pt<T>(-x, -y); }
  Pt<T> operator-(const Pt<T> pt) const { return Pt<T>(x - pt.x, y - pt.y); }
  Pt<T> operator*(T t) const { return Pt<T>(x * t, y * t); }
  Pt<T> operator/(T t) const { return Pt<T>(x / t, y / t); }
  T dot(Pt<T> v) const { return x * v.x + y * v.y; }
  T cross(Pt<T> v) const { return x * v.y - y * v.x; }
  T d2() { return x * x + y * y; }

  Pt<T> rot90() { return Pt<T>(-y, x); }

  bool operator<(const Pt& pt) const {
    return x < pt.x || (x == pt.x && y < pt.y);
  }

  void print() { printf("(%d,%d)", x, y); }
};

typedef Pt<int> pt;

struct UFT {
  int links[MAX_N], ranks[MAX_N], sizes[MAX_N];
  UFT() {}

  void init(int n) {
    for (int i = 0; i < n; i++) links[i] = i, ranks[i] = sizes[i] = 1;
  }

  int root(int i) {
    int i0 = i;
    while (links[i0] != i0) i0 = links[i0];
    return (links[i] = i0);
  }

  int rank(int i) { return ranks[root(i)]; }
  int size(int i) { return sizes[root(i)]; }
  bool same(int i, int j) { return root(i) == root(j); }

  int merge(int i0, int i1) {
    int r0 = root(i0), r1 = root(i1), mr;
    if (r0 == r1) return r0;
    if (ranks[r0] == ranks[r1]) {
      links[r1] = r0;
      sizes[r0] += sizes[r1];
      ranks[r0]++;
      mr = r0;
    }
    else if (ranks[r0] > ranks[r1]) {
      links[r1] = r0;
      sizes[r0] += sizes[r1];
      mr = r0;
    }
    else {
      links[r0] = r1;
      sizes[r1] += sizes[r0];
      mr = r1;
    }
    return mr;
  }
};

/* global variables */

pt ps[MAX_N], cs[MAX_M], vs[MAX_M];
vpii ess[MAX_M];
UFT uft;

/* subroutines */

void comb(int u, int k, int n, int bits, vi &cs) {
  if (k == 0) {
    cs.push_back(bits);
    return;
  }
  if (u >= n) return;

  for (int i = u, bi = 1 << i; i < n; i++, bi <<= 1)
    comb(i + 1, k - 1, n, bits | bi, cs);
}

/* main */

int main() {
  int n;
  scanf("%d", &n);

  for (int i = 0; i < n; i++)
    scanf("%d%d", &ps[i].x, &ps[i].y), ps[i] = ps[i] * 2;

  int m = 0;
  for (int i = 0; i < n; i++)
    for (int j = i + 1; j < n; j++) {
      pt c((ps[i] + ps[j]) / 2), v((ps[j] - ps[i]).rot90());
      int k = 0;
      for (; k < m; k++)
	if (vs[k].cross(v) == 0 && vs[k].cross(c - cs[k]) == 0)
	  break;
      if (k < m)
	ess[k].push_back(pii(i, j));
      else {
	cs[m] = c, vs[m] = v;
	ess[m++].push_back(pii(i, j));
      }
    }
  //printf("m=%d\n", m);

  for (int l = 1; l <= n - 2; l++) {
    vi cs;
    comb(0, l, m, 0, cs);

    for (vi::iterator vit = cs.begin(); vit != cs.end(); vit++) {
      int &bits = *vit;
      uft.init(n);

      for (int i = 0, bi = 1; i < m; i++, bi <<= 1)
	if (bits & bi) {
	  vpii &es = ess[i];
	  for (vpii::iterator eit = es.begin(); eit != es.end(); eit++)
	    uft.merge(eit->first, eit->second);
	}

      if (uft.size(0) == n) {
	printf("%d\n", l);
	return 0;
      }
    }
  }

  printf("%d\n", n - 1);
  return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 4980
final_rank: 4

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define SIZE 8

struct Point{
	Point(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	Point(){
		x = y = 0.0;
	}

	Point operator + (Point p){ return Point(x+p.x,y+p.y); }
	Point operator - (Point p){ return Point(x-p.x,y-p.y);}
	Point operator * (double a){ return Point(a*x,a*y); }
	Point operator / (double a){ return Point(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const Point &p) const{
		return x != p.x? x < p.x: y < p.y;
	}

	bool operator == (const Point &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}

	double x,y;
};

typedef Point Vector;

struct Line{
	Line(){

	}
	Line(Point a,Point b){
		p[0] = a;
		p[1] = b;
	}
	Point p[2];
};

int N;
int boss[8],height[8],table[30];
int REFLECT[8][30];
double NUM = 10000;
Point info[8];
vector<Line> LINE;

int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[x] > height[y]){

		boss[boss_y] = boss_x;

	}else if(height[x] < height[y]){

		boss[boss_x] = boss_y;

	}else{ //height[x] == height[y]

		boss[boss_y] = boss_x;
		height[x]++;
	}
}

void init(){

	for(int i = 0; i < N; i++){

		boss[i] = i;
		height[i] = 0;
	}
}


double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

double cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){
    return a.x*b.x + a.y*b.y;
}

Point calc_Reflection_Point(double x1,double y1,double x2,double y2,double xp,double yp){

	Point ret;

	bool X_FLG = false,Y_FLG = false;
	double slope;

	if(y1 == y2){
		X_FLG = true;
	}else if(x1 == x2){
		Y_FLG = true;
	}else{
		slope = (y2-y1)/(x2-x1);
	}

	if(X_FLG){
		ret.x = xp,ret.y=y1;
	}else if(Y_FLG){
		ret.x = x1,ret.y = yp;
	}else{
		ret.x = (yp*(x2-x1)*(y2-y1)+xp*(x2-x1)*(x2-x1)-y1*(y2-y1)*(x2-x1)+x1*(y2-y1)*(y2-y1))/((y2-y1)*(y2-y1)+(x2-x1)*(x2-x1));
		ret.y = ((x1-x2)*ret.x+yp*(y2-y1)+xp*(x2-x1))/(y2-y1);
	}
	ret.x = 2*ret.x-xp;
	ret.y = 2*ret.y-yp;

	return ret;
}

Point calc_Reflection_Point(Line line,Point point){

	return calc_Reflection_Point(line.p[0].x,line.p[0].y,line.p[1].x,line.p[1].y,point.x,point.y);
}

bool equals(Point A,Point B){

	return abs(A-B) < EPS;
}

double calc_slope(Line A){

	if(fabs(A.p[0].x-A.p[1].x) < EPS){

		return DBL_MAX;

	}else if(fabs(A.p[0].y-A.p[1].y) < EPS){

		return 0;

	}else{

		return (A.p[0].y-A.p[1].y)/(A.p[0].x-A.p[1].x);
	}
}

//点Aと点Bの垂直二等分線を求める
void calc_Vertical_Bisector(Point A,Point B){

	double first_slope = calc_slope(Line(A,B));
	Point mid_Point = Point((A.x+B.x)/2,(A.y+B.y)/2);

	Point another;

	if(fabs(first_slope) < EPS){ //AとBが水平に位置している場合

		another.x = mid_Point.x;
		another.y = mid_Point.y+NUM;

	}else if(fabs(first_slope-DBL_MAX) < EPS){ //AとBが垂直に位置している場合

		another.x = mid_Point.x+NUM;
		another.y = mid_Point.y;

	}else{ //その他

		double slope = -1.0/first_slope;

		another.x = mid_Point.x+NUM;
		another.y = mid_Point.y+NUM*slope;
	}
	LINE.push_back(Line(mid_Point,another));
}

bool dfs(vector<int> LINE_TO_USE,int index,int num_line){

	if(index == LINE.size()){

		if(LINE_TO_USE.size() != num_line)return false;

		init();

		for(int i = 0; i < N; i++){
			for(int k = 0; k < num_line; k++){

				if(REFLECT[i][LINE_TO_USE[k]] == -1)continue;

				unite(i,REFLECT[i][LINE_TO_USE[k]]);
			}
		}

		int num_group = 0;
		for(int i = 0; i < N; i++){
			if(i == get_boss(i)){

				num_group++;
			}
		}

		return num_group == 1; //全てが互いに行き来可能になったらOK
	}

	bool ret = dfs(LINE_TO_USE,index+1,num_line); ////LINE[index]を使わない
	if(ret){

		return true;
	}

	if(LINE_TO_USE.size() == num_line)return false; //これ以上増やせないならfalseをreturn

	//LINE[index]を使う
	vector<int> next_LINE_TO_USE = LINE_TO_USE;
	next_LINE_TO_USE.push_back(index);

	return dfs(next_LINE_TO_USE,index+1,num_line);
}

bool is_OK(int num_line){

	vector<int> LINE_TO_USE; //使う直線の集合

	return dfs(LINE_TO_USE,0,num_line);
}

//対称移動を事前計算しておく
void calc_Reflect(){

	for(int i = 0; i < N; i++){
		for(int k = 0; k < LINE.size(); k++){

			REFLECT[i][k] = -1;
		}
	}

	for(int i = 0; i < N; i++){
		for(int k = 0; k < LINE.size(); k++){

			Point tmp = calc_Reflection_Point(LINE[k],info[i]);

			for(int a = 0; a < N; a++){
				if(a == i)continue;

				if(equals(info[a],tmp)){

					REFLECT[i][k] = a;
				}
			}
		}
	}
}

int main(){

	scanf("%d",&N);

	for(int i = 0; i < N; i++){

		scanf("%lf %lf",&info[i].x,&info[i].y);
	}

	for(int i = 0; i < N-1; i++){
		for(int k = i+1; k < N; k++){
			calc_Vertical_Bisector(info[i],info[k]); //垂直二等分線をあらかじめ全計算しておく
		}
	}

	calc_Reflect();

	int minimum = N-1; //少なくともN-1本あればクリア可能

	for(int num_line = 1; num_line < minimum; num_line++){
		if(is_OK(num_line)){

			minimum = num_line;
			break;
		}
	}

	printf("%d\n",minimum);

	return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 3288
final_rank: 10

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define SIZE 8

struct Point{
	Point(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	Point(){
		x = y = 0.0;
	}

	Point operator + (Point p){ return Point(x+p.x,y+p.y); }
	Point operator - (Point p){ return Point(x-p.x,y-p.y);}
	Point operator * (double a){ return Point(a*x,a*y); }
	Point operator / (double a){ return Point(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const Point &p) const{
		return x != p.x? x < p.x: y < p.y;
	}

	bool operator == (const Point &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}

	double x,y;
};

typedef Point Vector;

struct Line{
	Line(){

	}
	Line(Point a,Point b){
		p[0] = a;
		p[1] = b;
	}
	Point p[2];
};

int N;
int boss[8],height[8],table[30];
int REFLECT[8][30];
double NUM = 10000;
Point info[8];
vector<Line> LINE;

int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[x] > height[y]){

		boss[boss_y] = boss_x;

	}else if(height[x] < height[y]){

		boss[boss_x] = boss_y;

	}else{ //height[x] == height[y]

		boss[boss_y] = boss_x;
		height[x]++;
	}
}

void init(){

	for(int i = 0; i < N; i++){

		boss[i] = i;
		height[i] = 0;
	}
}


double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

double cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){
    return a.x*b.x + a.y*b.y;
}

Point calc_Reflection_Point(double x1,double y1,double x2,double y2,double xp,double yp){

	Point ret;

	bool X_FLG = false,Y_FLG = false;
	double slope;

	if(y1 == y2){
		X_FLG = true;
	}else if(x1 == x2){
		Y_FLG = true;
	}else{
		slope = (y2-y1)/(x2-x1);
	}

	if(X_FLG){
		ret.x = xp,ret.y=y1;
	}else if(Y_FLG){
		ret.x = x1,ret.y = yp;
	}else{
		ret.x = (yp*(x2-x1)*(y2-y1)+xp*(x2-x1)*(x2-x1)-y1*(y2-y1)*(x2-x1)+x1*(y2-y1)*(y2-y1))/((y2-y1)*(y2-y1)+(x2-x1)*(x2-x1));
		ret.y = ((x1-x2)*ret.x+yp*(y2-y1)+xp*(x2-x1))/(y2-y1);
	}
	ret.x = 2*ret.x-xp;
	ret.y = 2*ret.y-yp;

	return ret;
}

Point calc_Reflection_Point(Line line,Point point){

	return calc_Reflection_Point(line.p[0].x,line.p[0].y,line.p[1].x,line.p[1].y,point.x,point.y);
}

bool equals(Point A,Point B){

	return abs(A-B) < EPS;
}

double calc_slope(Line A){

	if(fabs(A.p[0].x-A.p[1].x) < EPS){

		return DBL_MAX;

	}else if(fabs(A.p[0].y-A.p[1].y) < EPS){

		return 0;

	}else{

		return (A.p[0].y-A.p[1].y)/(A.p[0].x-A.p[1].x);
	}
}

//点Aと点Bの垂直二等分線を求める
void calc_Vertical_Bisector(Point A,Point B){

	double first_slope = calc_slope(Line(A,B));
	Point mid_Point = Point((A.x+B.x)/2,(A.y+B.y)/2);

	Point another;

	if(fabs(first_slope) < EPS){ //AとBが水平に位置している場合

		another.x = mid_Point.x;
		another.y = mid_Point.y+NUM;

	}else if(fabs(first_slope-DBL_MAX) < EPS){ //AとBが垂直に位置している場合

		another.x = mid_Point.x+NUM;
		another.y = mid_Point.y;

	}else{ //その他

		double slope = -1.0/first_slope;

		another.x = mid_Point.x+NUM;
		another.y = mid_Point.y+NUM*slope;
	}
	LINE.push_back(Line(mid_Point,another));
}

bool dfs(vector<int> LINE_TO_USE,int index,int num_line){

	if(index == LINE.size()){

		if(LINE_TO_USE.size() != num_line)return false;

		init();

		for(int i = 0; i < N; i++){
			for(int k = 0; k < num_line; k++){

				if(REFLECT[i][LINE_TO_USE[k]] == -1)continue;

				unite(i,REFLECT[i][LINE_TO_USE[k]]);
			}
		}

		int num_group = 0;
		for(int i = 0; i < N; i++){
			if(i == get_boss(i)){

				num_group++;
			}
		}

		return num_group == 1; //全てが互いに行き来可能になったらOK
	}

	bool ret = dfs(LINE_TO_USE,index+1,num_line); ////LINE[index]を使わない
	if(ret){

		return true;
	}

	if(LINE_TO_USE.size() == num_line)return false; //これ以上増やせないならfalseをreturn

	//LINE[index]を使う
	vector<int> next_LINE_TO_USE = LINE_TO_USE;
	next_LINE_TO_USE.push_back(index);

	return dfs(next_LINE_TO_USE,index+1,num_line);
}

bool is_OK(int num_line){

	vector<int> LINE_TO_USE; //使う直線の集合

	return dfs(LINE_TO_USE,0,num_line);
}

//対称移動を事前計算しておく
void calc_Reflect(){

	for(int i = 0; i < N; i++){
		for(int k = 0; k < LINE.size(); k++){

			REFLECT[i][k] = -1;
		}
	}

	for(int i = 0; i < N; i++){
		for(int k = 0; k < LINE.size(); k++){

			Point tmp = calc_Reflection_Point(LINE[k],info[i]);

			for(int a = 0; a < N; a++){
				if(a == i)continue;

				if(equals(info[a],tmp)){

					REFLECT[i][k] = a;
				}
			}
		}
	}
}

int main(){

	scanf("%d",&N);

	for(int i = 0; i < N; i++){

		scanf("%lf %lf",&info[i].x,&info[i].y);
	}

	for(int i = 0; i < N-1; i++){
		for(int k = i+1; k < N; k++){
			calc_Vertical_Bisector(info[i],info[k]); //垂直二等分線をあらかじめ全計算しておく
		}
	}

	calc_Reflect();

	int minimum = N-1; //少なくともN-1本あればクリア可能

	for(int num_line = 0; num_line < minimum; num_line++){ //星が近い場合がある?
		if(is_OK(num_line)){

			minimum = num_line;
			break;
		}
	}

	printf("%d\n",minimum);

	return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 3276
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
typedef P Vector;

typedef pair<P,Vector> Line;

int gcd(int a,int b){
	if(b==0)return a;
	return gcd(b,a%b);
}

int dot(Vector a,Vector b){
	return a.first*a.second+b.first*b.second;
}

int cross(Vector a,Vector b){
	return a.first*b.second-b.first*a.second;
}

int n;
int x[9],y[9];
int ans;
vector<Line> li;
bool po=false;

bool same(Line a,Line b){
	if(a.second.first==0){
		if(a.first.first==b.first.first && b.second.first==0)return true;
	}else if(a.second.second==0){
		if(a.first.second==b.first.second && b.second.second==0)return true;
	}else{
		int diff=b.first.first-a.first.first;
		if(diff%a.second.first!=0)return false;
		int bi=diff/a.second.first;
		if(cross(a.second,b.second)==0 && a.first.second+bi*a.second.second==b.first.second)return true;
	}
	return false;
}

void dfs(int bit){
	//printf("%d\n",bit);
	if(bit==(1<<n)-1){
		ans=min(ans,(int)li.size());
		return;
	}
	if(ans<=li.size())return;
	for(int i=0;i<n;i++){
		if(bit>>i & 1){
			for(int j=0;j<n;j++){
				if(!(bit>>j & 1)){
					P mid=P((x[i]+x[j])/2,(y[i]+y[j])/2);
					Vector vec=Vector(y[j]-y[i],x[i]-x[j]);
					int g=gcd(abs(y[j]-y[i]),abs(x[i]-x[j]));
					//printf("%d %d %d\n",abs(y[j]-y[i]),abs(x[i]-y[j]),g);
					vec.first/=g;
					vec.second/=g;
					bool flag=false;
					for(int k=0;k<li.size();k++){
						if(same(li[k],Line(mid,vec))){
							flag=true;
							break;
						}
					}
					if(!flag){
						li.push_back(Line(mid,vec));
					}
					dfs(bit+(1<<j));
					if(!flag){
						li.pop_back();
					}
				}
			}
		}
	}
}

int main(void){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d%d",&x[i],&y[i]);
		x[i]*=2;
		y[i]*=2;
	}
	vector<Line> li;
	bool po=false;
	for(int i=0;i<n;i++){
		for(int j=i+1;j<n;j++){
			P mid=P((x[i]+x[j])/2,(y[i]+y[j])/2);
			Vector vec=Vector(y[j]-y[i],x[i]-x[j]);
			int g=gcd(abs(y[j]-y[i]),abs(x[i]-x[j]));
			vec.first/=g;
			vec.second/=g;
			for(int k=0;k<n;k++){
				if(k==i)continue;
				for(int l=k+1;l<n;l++){
					if(l==j)continue;
					P mid2=P((x[k]+x[l])/2,(y[k]+y[l])/2);
					Vector vec2=Vector(y[l]-y[k],x[k]-x[l]);
					g=gcd(abs(y[l]-y[k]),abs(x[l]-x[k]));
					vec2.first/=g;
					vec2.second/=g;
					if(same(Line(mid,vec),Line(mid2,vec2)))po=true;
				}
			}
		}
	}
	if(!po){
		printf("%d\n",n-1);
		return 0;
	}
	ans=n-2;
	dfs(1);
	printf("%d\n",ans);
	return 0;
}
accuracy: 1
time_ms: 350
memory_kb: 3208
final_rank: 11

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define repl(i,a,b) for(int i=(int)(a);i<(int)(b);i++)
#define rep(i,n) repl(i,0,n)
#define all(x) (x).begin(),(x).end()
#define mmax(x,y) (x>y?x:y)
#define mmin(x,y) (x<y?x:y)
#define maxch(x,y) x=mmax(x,y)
#define minch(x,y) x=mmin(x,y)
#define uni(x) x.erase(unique(all(x)),x.end())
#define exist(x,y) (find(all(x),y)!=x.end())
#define bcnt __builtin_popcount

#define dbg(...) _dbg(#__VA_ARGS__, __VA_ARGS__)
void _dbg(string){cerr<<endl;}
template<class H,class... T> void _dbg(string s,H h,T... t){int l=s.find(',');cerr<<s.substr(0,l)<<" = "<<h<<", ";_dbg(s.substr(l+1),t...);}
template<class T,class U> ostream& operator<<(ostream &o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;}
template<class T> ostream& operator<<(ostream &o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;}


class UnionFind {
public:
  int par[8], rank[8];
  UnionFind(){
    rep(i,8) par[i] = -1;
    rep(i,8) rank[i] = 0;
  }
  int find(int x){
    if(par[x]<0) return x;
    else return par[x] = find(par[x]);
  }
  void unite(int x, int y){
    x=find(x); y=find(y);
    if(x==y) return;
    if(rank[x] < rank[y]) swap(x,y);
    par[x] += par[y];
    par[y] = x;
    if(rank[x]==rank[y]) rank[x]++;
  }
  inline bool same(int x, int y){ return find(x) == find(y); }
  inline int size(int x){ return -par[find(x)]; }
};

int n;
int x[101],y[101];

int main(){
  scanf("%d",&n);
  rep(i,n) scanf("%d%d",x+i,y+i);

  using P = pair<int,int>;
  using PP = pair<P,int>;
  map<PP, vector<P>> lines;
  rep(i,n) repl(j,i+1,n){
    int dx=x[i]-x[j];
    int dy=y[i]-y[j];
    swap(dx,dy);
    dy = -dy;
    PP cp;
    if(dy==0){
      cp = PP({1,0}, y[i]+y[j]);
    }
    else if(dx==0){
      cp = PP({0,1}, x[i]+x[j]);
    }
    else{
      int g = __gcd(dx,dy);
      dx /= g; dy /= g;
      if(dx<0){ dx=-dx; dy = -dy; }
      cp = PP({dx,dy}, (y[i]+y[j])*dx - (x[i]+x[j])*dy);
    }
    lines[cp].push_back({i,j});
  }

  map<vector<int>, int> dp;
  vector<int> init(n);
  rep(i,n) init[i] = i;

  dp[init] = 0;
  int ref[8];
  for(auto &vp : lines){
    map<vector<int>, int> nxt(dp);
    for(auto &sv : dp){
      UnionFind uf;
      rep(i,n) uf.unite(i, sv.first[i]);
      for(auto &p : vp.second) uf.unite(p.first, p.second);

      rep(i,n) ref[i] = i;
      rep(i,n) ref[uf.find(i)] = min(i, ref[uf.find(i)]);
      vector<int> ns(n);
      rep(i,n) ns[i] = ref[uf.find(i)];

      auto itr = nxt.find(ns);
      if(itr == nxt.end()) nxt[ns] = sv.second + 1;
      else itr->second = min(itr->second, sv.second + 1);
    }
    swap(dp, nxt);
  }

  vector<int> fin(n,0);
  cout << dp[fin] << endl;

  return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 4220
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define repl(i,a,b) for(int i=(int)(a);i<(int)(b);i++)
#define rep(i,n) repl(i,0,n)
#define all(x) (x).begin(),(x).end()
#define dbg(x) cout<<#x"="<<x<<endl
#define mmax(x,y) (x>y?x:y)
#define mmin(x,y) (x<y?x:y)
#define maxch(x,y) x=mmax(x,y)
#define minch(x,y) x=mmin(x,y)
#define uni(x) x.erase(unique(all(x)),x.end())
#define exist(x,y) (find(all(x),y)!=x.end())
#define bcnt __builtin_popcount

#define INF 1e16
#define mod 1000000007

int n;
int x[101],y[101];

int main(){
  scanf("%d",&n);
  rep(i,n) scanf("%d%d",x+i,y+i);

  vector<int> idx;
  rep(i,n) idx.push_back(i);
  int res=1000000;

  using PP = pair<pair<int,int>,int>;
  do {
    set<PP> as;
    rep(i,n-1){
      int dx=x[idx[i+1]]-x[idx[i]];
      int dy=y[idx[i+1]]-y[idx[i]];
      swap(dx,dy);
      dy = -dy;
      if(dy==0){
        auto cp = PP({1,0}, y[idx[i]]+y[idx[i+1]]);
        as.insert(cp);
      }
      else if(dx==0){
        auto cp = PP({0,1}, x[idx[i]]+x[idx[i+1]]);
        as.insert(cp);
      }
      else{
        int g = __gcd(dx,dy);
        dx /= g; dy /= g;
        if(dx<0){ dx=-dx; dy = -dy; }
        auto cp = PP({dx,dy}, (y[idx[i]]+y[idx[i+1]])*dx - (x[idx[i]]+x[idx[i+1]])*dy);
        as.insert(cp);
      }
    }
    minch(res,(int)as.size());
  }while(next_permutation(all(idx)));
  cout<<res<<endl;
  return 0;
}
accuracy: 0.3939393939393939
time_ms: 10
memory_kb: 3228
final_rank: 17

code_snippet: #include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<double, double> P;

#define fi first
#define se second
#define repl(i,a,b) for(ll i=(ll)(a);i<(ll)(b);i++)
#define rep(i,n) repl(i,0,n)
#define all(x) (x).begin(),(x).end()
#define dbg(x) cout<<#x"="<<x<<endl
#define mmax(x,y) (x>y?x:y)
#define mmin(x,y) (x<y?x:y)
#define maxch(x,y) x=mmax(x,y)
#define minch(x,y) x=mmin(x,y)
#define uni(x) x.erase(unique(all(x)),x.end())
#define exist(x,y) (find(all(x),y)!=x.end())
#define bcnt __builtin_popcount

#define INF 1e16
#define mod 1000000007

int n;
double x[101],y[101];

int main(){
  scanf("%d",&n);
  rep(i,n){
    scanf("%lf%lf",x+i,y+i);
  }
  vector<int> idx;
  rep(i,n)idx.push_back(i);
  int res=1000000;
  do{
    int sum=0;
    set<P> as;
    rep(i,n-1){
      double dx=x[idx[i+1]]-x[idx[i]];
      double dy=y[idx[i+1]]-y[idx[i]];
      if(dy==0){
        double midx=(double)(x[idx[i+1]]+x[idx[i]])/2.0;
        if(as.find(P(1e18,midx))!=as.end())continue;
        as.insert(P(1e18,midx));
        sum++;
      }else if(dx==0){
        double midy=(double)(y[idx[i+1]]+y[idx[i]])/2.0;
        if(as.find(P(0,midy))!=as.end())continue;
        as.insert(P(0,midy));
        sum++;
      }else{
        double a=-1.0/(dy/dx);
        double midx=(double)(x[idx[i+1]]+x[idx[i]])/2.0;
        double midy=(double)(y[idx[i+1]]+y[idx[i]])/2.0;
        double b=midy-midx*a;
        if(as.find(P(a,b))!=as.end())continue;
        as.insert(P(a,b));
        sum++;
      }
    }
    minch(res,sum);
  }while(next_permutation(all(idx)));
  cout<<res<<endl;
  return 0;
}
accuracy: 0.3939393939393939
time_ms: 10
memory_kb: 3280
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;
double INF=1<<29;
typedef long long ll;
typedef complex<double> P;
typedef pair<int,int> PP;
namespace std {
  bool operator < (const P& a, const P& b) {
    return real(a)!=real(b)?real(a)<real(b):imag(a)<imag(b);
  }
}
int p[10],r[10];
void init(){for(int i=0;i<10;i++)p[i]=i,r[i]=0;}
int find(int x){return (p[x]==x)?x:(p[x]=find(p[x]));}
void unite(int x,int y) {
  x=find(x),y=find(y);
  if(x==y)return;
  if(r[x]<r[y])p[x]=y;
  else{p[y]=x;if(r[x]==r[y])r[x]++;}
}
bool same(int x,int y){return find(x)==find(y);}

P PerpendicularBisector(P a,P b) {
  double x,y;
  if(b.imag()-a.imag()) {
    x=-(b.real()-a.real())/(b.imag()-a.imag());
    y=-(a.real()+b.real())/2*x+(a.imag()+b.imag())/2;
  } else {
    x=INF;
    y=(a.real()+b.real())/2;
  }
  if(x==-0) x=0;
  return P(x,y);
}

P a[10];
ll n,k,ans;
map<P,vector<PP> > m;
map<int,P> ma;
void dfs(int l, ll t) {
  bool f=1;
  for(int i=1; i<n; i++) {
    if(!same(0,i)) {
      f=0;
      break;
    }
  }
  if(f) ans=min(ans,t);
  if(t>=n-2) return;
  for(int i=l; i<k; i++) {
    int pp[n],rr[n];
    for(int j=0; j<n; j++) pp[j]=p[j],rr[j]=r[j];
    vector<PP> v=m[ma[i]];
    for(int j=0; j<v.size(); j++) unite(v[j].first,v[j].second);
    dfs(i+1,t+1);
    for(int j=0; j<n; j++) p[j]=pp[j],r[j]=rr[j];
  }
}

int main() {
  cin >> n;
  for(int i=0,x,y; i<n; i++) {
    cin >> x >> y;
    a[i]=P(x,y);
  }
  for(int i=0; i<n; i++) {
    for(int j=i+1; j<n; j++) m[PerpendicularBisector(a[i],a[j])].push_back(PP(i,j));
  }
  k=0;
  for(map<P,vector<PP> >::iterator it=m.begin();it!=m.end();it++) ma[k++]=it->first;
  init();
  ans=n-1;
  dfs(0,0);
  cout << ans << endl;
  return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3136
final_rank: 2

code_snippet: #include <bits/stdc++.h>
#define int long long
  
#define _overload(_1,_2,_3,name,...) name
#define _rep(i,n) _range(i,0,n)
#define _range(i,a,b) for(int i=(int)(a);i<(int)(b);++i)
#define rep(...) _overload(__VA_ARGS__,_range,_rep,)(__VA_ARGS__)
  
#define _rrep(i,n) _rrange(i,n,0)
#define _rrange(i,a,b) for(int i=(int)(a)-1;i>=(int)(b);--i)
#define rrep(...) _overload(__VA_ARGS__,_rrange,_rrep,)(__VA_ARGS__)
  
#define _all(arg) begin(arg),end(arg)
#define uniq(arg) sort(_all(arg)),(arg).erase(unique(_all(arg)),end(arg))
#define getidx(ary,key) lower_bound(_all(ary),key)-begin(ary)
#define clr(a,b) memset((a),(b),sizeof(a))
#define bit(n) (1LL<<(n))
  
// #define DEBUG
  
#ifdef DEBUG
    #define dump(...) fprintf(stderr, __VA_ARGS__)
#else
    #define dump(...)
#endif
  
template<class T>bool chmax(T &a, const T &b) { return (a<b)?(a=b,1):0;}
template<class T>bool chmin(T &a, const T &b) { return (b<a)?(a=b,1):0;}
  
using namespace std;
using ll=long long;
using vi=vector<int>;
using vll=vector<ll>;
  
const double EPS = 1e-9;
const double PI = acos(-1.0);
const ll inf =1LL << 62;
const ll mod=1000000007LL;
const int dx[4]={1,0,-1,0};
const int dy[4]={0,1,0,-1};
  
  
ll extgcd(ll a,ll b,ll& x,ll& y){x=1,y=0;ll g=a;if(b!=0) g=extgcd(b,a%b,y,x),y-=a/b*x;return g;}
ll ADD(const ll &a, const ll &b,const ll &mod) { return (a+b)%mod;}
ll SUB(const ll &a, const ll &b,const ll &mod) { return (a-b+mod)%mod;}
ll MUL(const ll &a, const ll &b,const ll &mod) { return (1LL*a*b)%mod;}
ll DIV(const ll &a, const ll &b,const ll &mod) {ll x,y; extgcd(b,mod,x,y);return MUL(a,(x+mod)%mod,mod);}
  
random_device rd;
mt19937 mt(rd());
uniform_int_distribution<int> dice(1,6);
uniform_real_distribution<double> score(0.0,10.0);
  
using E = tuple<int, int>;
using R = double;
using P = complex<R>;
using L = tuple<P, P>;
 
inline R dt(P a, P b){
	return imag(conj(a) * b);
}

bool match(L lhs, L rhs){
    P a1, a2, b1, b2;
    tie(a1, a2) = lhs;
    tie(b1, b2) = rhs;
 
    if(abs(dt(a2 - a1, b2 - b1)) > EPS) return false;
    if(abs(dt(a2 - a1, b2 - a1)) > EPS) return false;
 
    return true;
}
 
L make_line(P a, P b){
	P mid = (a + b) / P(2, 0);
    
    return L(mid, mid + (b - a) * P(0, 1));
}

int n, res;
vector<P> p;

struct UnionFind {
    vector<int> data;
    UnionFind(int size) : data(size, -1) { }
    bool unionSet(int x, int y) {
        x = root(x); y = root(y);
        if (x != y) {
            if (data[y] < data[x]) swap(x, y);
            data[x] += data[y]; data[y] = x;
        }
        return x != y;
    }
    bool findSet(int x, int y) {
        return root(x) == root(y);
    }
    int root(int x) {
        return data[x] < 0 ? x : data[x] = root(data[x]);
    }
    int size(int x) {
        return -data[root(x)];
    }
};

bool check_tree(vector<E> & edges){
	UnionFind uf(n);

	for(auto & e : edges){
		int a, b; tie(a, b) = e;
		uf.unionSet(a, b);
	}

	rep(i, n){
		if(uf.root(0) != uf.root(i)) return false;
	}
	return true;
}

signed main(void){
    cin >> n;
    p = vector<P>(n);
    for(auto & e : p){
        R x, y; cin >> x >> y;
        e = P(x, y);
    }

	vector<E> tbl;
	rep(a, n){
		rep(b, a + 1, n){
			tbl.push_back(E(a, b));
		}
	}

	int res = n - 1;

	int k = n - 1;
	int comb = (1 << k) - 1;
	int len = tbl.size();

	while(comb < 1 << len){
		vector<E> edges;
		rep(i, len){
			if(((comb >> i) & 1) == 0) continue;

			edges.push_back(tbl[i]);
		}

		int x = comb & -comb, y = comb + x;
		comb = (((comb & ~y) / x) >> 1) | y;

		if(not check_tree(edges)) continue;

		vector<L> s;
		for(auto & e : edges){
			int a, b; tie(a, b) = e;

			L cline = make_line(p[a], p[b]);
			[&]{
				for(auto & l : s){
					if(match(l, cline)) return;
				}
				s.push_back(cline);
			}();
		}

//		if(s.size() == 2){
//			for(auto & e : edges){
//				int a, b; tie(a, b) = e;
//				cerr << a << " " << b << endl;
//
//				L cline = make_line(p[a], p[b]);
//				P p1, p2; tie(p1, p2) = cline;
//				cerr << fixed << real(p1) << " " << imag(p1) << endl;
//				cerr << fixed << real(p2) << " " << imag(p2) << endl;
//			}
//			cerr << "---" << endl;
//		}

		chmin(res, (int)s.size());
	}

	cout << res << endl;
 
    return 0;
}
accuracy: 1
time_ms: 450
memory_kb: 3244
final_rank: 12

code_snippet: #include <bits/stdc++.h>
#define int long long
  
#define _overload(_1,_2,_3,name,...) name
#define _rep(i,n) _range(i,0,n)
#define _range(i,a,b) for(int i=(int)(a);i<(int)(b);++i)
#define rep(...) _overload(__VA_ARGS__,_range,_rep,)(__VA_ARGS__)
  
#define _rrep(i,n) _rrange(i,n,0)
#define _rrange(i,a,b) for(int i=(int)(a)-1;i>=(int)(b);--i)
#define rrep(...) _overload(__VA_ARGS__,_rrange,_rrep,)(__VA_ARGS__)
  
#define _all(arg) begin(arg),end(arg)
#define uniq(arg) sort(_all(arg)),(arg).erase(unique(_all(arg)),end(arg))
#define getidx(ary,key) lower_bound(_all(ary),key)-begin(ary)
#define clr(a,b) memset((a),(b),sizeof(a))
#define bit(n) (1LL<<(n))
  
// #define DEBUG
  
#ifdef DEBUG
    #define dump(...) fprintf(stderr, __VA_ARGS__)
#else
    #define dump(...)
#endif
  
template<class T>bool chmax(T &a, const T &b) { return (a<b)?(a=b,1):0;}
template<class T>bool chmin(T &a, const T &b) { return (b<a)?(a=b,1):0;}
  
using namespace std;
using ll=long long;
using vi=vector<int>;
using vll=vector<ll>;
  
const double EPS = 1e-9;
const double PI = acos(-1.0);
const ll inf =1LL << 62;
const ll mod=1000000007LL;
const int dx[4]={1,0,-1,0};
const int dy[4]={0,1,0,-1};
  
  
ll extgcd(ll a,ll b,ll& x,ll& y){x=1,y=0;ll g=a;if(b!=0) g=extgcd(b,a%b,y,x),y-=a/b*x;return g;}
ll ADD(const ll &a, const ll &b,const ll &mod) { return (a+b)%mod;}
ll SUB(const ll &a, const ll &b,const ll &mod) { return (a-b+mod)%mod;}
ll MUL(const ll &a, const ll &b,const ll &mod) { return (1LL*a*b)%mod;}
ll DIV(const ll &a, const ll &b,const ll &mod) {ll x,y; extgcd(b,mod,x,y);return MUL(a,(x+mod)%mod,mod);}
  
random_device rd;
mt19937 mt(rd());
uniform_int_distribution<int> dice(1,6);
uniform_real_distribution<double> score(0.0,10.0);
  
using E = tuple<int, int>;
using R = double;
using P = complex<R>;
using L = tuple<P, P>;
 
inline R dt(P a, P b){
	return imag(conj(a) * b);
}

bool match(L lhs, L rhs){
    P a1, a2, b1, b2;
    tie(a1, b1) = lhs;
    tie(a2, b2) = rhs;
 
    if(abs(dt(a2 - a1, b2 - b1)) > EPS) return false;
    if(abs(dt(a2 - a1, b2 - a1)) > EPS) return false;
 
    return true;
}
 
L make_line(P a, P b){
	P mid = (a + b) / P(2, 0);
    
    return L(mid, mid + (b - a) * P(0, 1));
}

int n, res;
vector<P> p;

struct UnionFind {
    vector<int> data;
    UnionFind(int size) : data(size, -1) { }
    bool unionSet(int x, int y) {
        x = root(x); y = root(y);
        if (x != y) {
            if (data[y] < data[x]) swap(x, y);
            data[x] += data[y]; data[y] = x;
        }
        return x != y;
    }
    bool findSet(int x, int y) {
        return root(x) == root(y);
    }
    int root(int x) {
        return data[x] < 0 ? x : data[x] = root(data[x]);
    }
    int size(int x) {
        return -data[root(x)];
    }
};

bool check_tree(vector<E> & edges){
	UnionFind uf(n);

	for(auto & e : edges){
		int a, b; tie(a, b) = e;
		uf.unionSet(a, b);
	}

	rep(i, n){
		if(uf.root(0) != uf.root(i)) return false;
	}
	return true;
}

signed main(void){
    cin >> n;
    p = vector<P>(n);
    for(auto & e : p){
        int x, y; cin >> x >> y;
        e = P(x, y);
    }

	vector<E> tbl;
	rep(a, n){
		rep(b, a + 1, n){
			tbl.push_back(E(a, b));
		}
	}

	int res = n - 1;

	int k = n - 1;
	int comb = (1 << k) - 1;
	int len = tbl.size();

	while(comb < 1 << len){
		vector<E> edges;
		rep(i, len){
			if(((comb >> i) & 1) == 0) continue;

			edges.push_back(tbl[i]);
		}

		int x = comb & -comb, y = comb + x;
		comb = (((comb & ~y) / x) >> 1) | y;

		if(not check_tree(edges)) continue;

		vector<L> s;
		for(auto & e : edges){
			int a, b; tie(a, b) = e;

			L cline = make_line(p[a], p[b]);
			[&]{
				for(auto & l : s){
					if(match(l, cline)) return;
				}
				s.push_back(cline);
			}();
		}

		chmin(res, (int)s.size());
	}

	cout << res << endl;
 
    return 0;
}
accuracy: 0.18181818181818182
time_ms: 20
memory_kb: 3144
final_rank: 19

code_snippet: #include <bits/stdc++.h>
#define int long long
  
#define _overload(_1,_2,_3,name,...) name
#define _rep(i,n) _range(i,0,n)
#define _range(i,a,b) for(int i=(int)(a);i<(int)(b);++i)
#define rep(...) _overload(__VA_ARGS__,_range,_rep,)(__VA_ARGS__)
  
#define _rrep(i,n) _rrange(i,n,0)
#define _rrange(i,a,b) for(int i=(int)(a)-1;i>=(int)(b);--i)
#define rrep(...) _overload(__VA_ARGS__,_rrange,_rrep,)(__VA_ARGS__)
  
#define _all(arg) begin(arg),end(arg)
#define uniq(arg) sort(_all(arg)),(arg).erase(unique(_all(arg)),end(arg))
#define getidx(ary,key) lower_bound(_all(ary),key)-begin(ary)
#define clr(a,b) memset((a),(b),sizeof(a))
#define bit(n) (1LL<<(n))
  
// #define DEBUG
  
#ifdef DEBUG
    #define dump(...) fprintf(stderr, __VA_ARGS__)
#else
    #define dump(...)
#endif
  
template<class T>bool chmax(T &a, const T &b) { return (a<b)?(a=b,1):0;}
template<class T>bool chmin(T &a, const T &b) { return (b<a)?(a=b,1):0;}
  
using namespace std;
using ll=long long;
using vi=vector<int>;
using vll=vector<ll>;
  
const double EPS = 1e-10;
const double PI = acos(-1.0);
const ll inf =1LL << 62;
const ll mod=1000000007LL;
const int dx[4]={1,0,-1,0};
const int dy[4]={0,1,0,-1};
  
  
ll extgcd(ll a,ll b,ll& x,ll& y){x=1,y=0;ll g=a;if(b!=0) g=extgcd(b,a%b,y,x),y-=a/b*x;return g;}
ll ADD(const ll &a, const ll &b,const ll &mod) { return (a+b)%mod;}
ll SUB(const ll &a, const ll &b,const ll &mod) { return (a-b+mod)%mod;}
ll MUL(const ll &a, const ll &b,const ll &mod) { return (1LL*a*b)%mod;}
ll DIV(const ll &a, const ll &b,const ll &mod) {ll x,y; extgcd(b,mod,x,y);return MUL(a,(x+mod)%mod,mod);}
  
random_device rd;
mt19937 mt(rd());
uniform_int_distribution<int> dice(1,6);
uniform_real_distribution<double> score(0.0,10.0);
  
using E = tuple<int, int>;
using R = double;
using P = complex<R>;
using L = tuple<P, P>;
 
inline R dt(P a, P b){
	return imag(conj(a) * b);
}

bool match(L lhs, L rhs){
    P a1, a2, b1, b2;
    tie(a1, b1) = lhs;
    tie(a2, b2) = rhs;
 
    if(abs(dt(a2 - a1, b2 - b1)) > EPS) return false;
    if(abs(dt(a2 - a1, b2 - a1)) > EPS) return false;
 
    return true;
}
 
L make_line(P a, P b){
	P mid = (a + b) / P(2, 0);
    
    return L(mid, mid + (b - a) * P(0, 1));
}

int n, res;
vector<P> p;

struct UnionFind {
    vector<int> data;
    UnionFind(int size) : data(size, -1) { }
    bool unionSet(int x, int y) {
        x = root(x); y = root(y);
        if (x != y) {
            if (data[y] < data[x]) swap(x, y);
            data[x] += data[y]; data[y] = x;
        }
        return x != y;
    }
    bool findSet(int x, int y) {
        return root(x) == root(y);
    }
    int root(int x) {
        return data[x] < 0 ? x : data[x] = root(data[x]);
    }
    int size(int x) {
        return -data[root(x)];
    }
};

bool check_tree(vector<E> & edges){
	UnionFind uf(n);

	for(auto & e : edges){
		int a, b; tie(a, b) = e;
		uf.unionSet(a, b);
	}

	rep(i, n){
		if(uf.root(0) != uf.root(i)) return false;
	}
	return true;
}

signed main(void){
    cin >> n;
    p = vector<P>(n);
    for(auto & e : p){
        int x, y; cin >> x >> y;
        e = P(x, y);
    }

	vector<E> tbl;
	rep(a, n){
		rep(b, a + 1, n){
			tbl.push_back(E(a, b));
		}
	}

	int res = n - 1;

	int k = n - 1;
	int comb = (1 << k) - 1;
	int len = tbl.size();

	while(comb < 1 << len){
		vector<E> edges;
		rep(i, len){
			if(((comb >> i) & 1) == 0) continue;

			edges.push_back(tbl[i]);
		}

		int x = comb & -comb, y = comb + x;
		comb = (((comb & ~y) / x) >> 1) | y;

		if(not check_tree(edges)) continue;

		vector<L> s;
		for(auto & e : edges){
			int a, b; tie(a, b) = e;

			L cline = make_line(p[a], p[b]);
			[&]{
				for(auto & l : s){
					if(match(l, cline)) return;
				}
				s.push_back(cline);
			}();
		}

		chmin(res, (int)s.size());
	}

	cout << res << endl;
 
    return 0;
}
accuracy: 0.18181818181818182
time_ms: 20
memory_kb: 3184
final_rank: 20

code_snippet: #include<bits/stdc++.h>

#define rep(i,n) for(int i=0;i<(int)n;i++)
#define all(c) (c).begin(),(c).end()
#define mp make_pair
#define pb push_back
#define each(i,c) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();i++)
#define dbg(x) cerr<<__LINE__<<": "<<#x<<" = "<<(x)<<endl

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> pi;
const int inf = (int)1e9;
const double EPS = 1e-8, INF = 1e12, PI = acos(-1.0);
typedef complex<double> P;
namespace std{
	bool operator<(const P& a, const P& b){
		return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b);
	}
}
inline double cross(const P& a, const P& b){ return imag(conj(a)*b); }
inline double dot(const P& a, const P& b){ return real(conj(a)*b); }
struct L : public vector<P>{
	L(const P &a, const P &b) {
		push_back(a); push_back(b);
	}
};
typedef vector<P> G;

inline int ccw(P a, P b, P c) {
	b -= a; c -= a;
	if(cross(b, c) > 0)   return +1;		// counter clockwise
	if(cross(b, c) < 0)   return -1;		// clockwise
	if(dot(b, c) < 0)     return +2;		// c--a--b on line
	if(norm(b) < norm(c)) return -2;		// a--b--c on line
	return 0;
}

int n, x[8], y[8];
vector<pi> t[8][8];
map<vi, int> dp;

int root(vi &p, int x){
	return x == p[x] ? x : p[x] = root(p, p[x]);
}

int rec(vi v){
	if(dp.count(v)) return dp[v];
	
	int &res = dp[v];
	int fin = 0;
	rep(i, n) fin = max(fin, v[i]);
	if(fin == 0) return 0;
	
	res = inf;
	rep(i, n) rep(j, i){
		vi uf, to(n, -1);
		rep(k, fin + 1) uf.pb(k);
		
		for(pi p : t[i][j]){
			int a = root(uf, v[p.first]), b = root(uf, v[p.second]);
			if(a != b) uf[b] = a;
		}
		int sz = 0;
		rep(k, n) if(to[root(uf, v[k])] < 0) to[root(uf, v[k])] = sz++;
		
		vi nxt;
		rep(k, n) nxt.pb(to[root(uf, v[k])]);
		
		if(v != nxt) res = min(res, rec(nxt) + 1);
	}
	return res;
}
int main(){
	cin >> n;
	rep(i, n) cin >> x[i] >> y[i];
	rep(i, n) rep(j, i){
		P p((x[i] + x[j]) / 2.0, (y[i] + y[j]) / 2.0), q(x[j] - x[i], y[j] - y[i]);
		q *= P(0, 1);
		t[i][j].pb(mp(i, j));
		rep(k, n) rep(l, k) if(k != i || l != j){
			P r((x[k] + x[l]) / 2.0, (y[k] + y[l]) / 2.0), s(x[l] - x[k], y[l] - y[k]);
			s *= P(0, 1);
			if(abs(cross(q, s)) < EPS && abs(cross(p - r, q)) < EPS){
				
				t[i][j].pb(mp(k, l));
			}
		}
	}
	vi init;
	rep(i, n) init.pb(i);
	cout << rec(init) << endl;
	return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 3660
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;

typedef long long ll;

template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
  if (!v.empty()) {
    out << '[';
    std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
    out << "\b\b]";
  }
  return out;
}
template <class T, class U>
void chmin(T& t, U f) {
  if (t > f) t = f;
}
template <class T, class U>
void chmax(T& t, U f) {
  if (t < f) t = f;
}

struct UnionFind {
  vector<int> data;
  UnionFind(int size) : data(size, -1) {}
  UnionFind(const UnionFind& u) { data = u.data; }
  bool unionSet(int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) {
      if (data[y] < data[x]) swap(x, y);
      data[x] += data[y];
      data[y] = x;
    }
    return x != y;
  }
  bool findSet(int x, int y) { return root(x) == root(y); }
  int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
  int size(int x) { return -data[root(x)]; }
};

int N;
vector<int> x, y;
map<pair<int, int>, vector<pair<int, int>>> m;

void hoge(int i, int j) {
  //-2x(x2-x1)-2y(y2-y1)+()+()
  ll a = -2 * (x[j] - x[i]);
  ll b = -2 * (y[j] - y[i]);
  ll c = (x[j] * x[j] - x[i] * x[i]) + (y[j] * y[j] - y[i] * y[i]);
  for (int k = 0; k < N; ++k) {
    for (int l = k + 1; l < N; ++l) {
      if (abs(a * x[l] + b * y[l] + c) == abs(a * x[k] + b * y[k] + c) &&
          (x[k] - x[l]) * (y[i] - y[j]) == (y[k] - y[l]) * (x[i] - x[j])) {
        m[{i, j}].push_back({k, l});
      }
    }
  }
}

int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cin >> N;
  x.resize(N), y.resize(N);
  for (int i = 0; i < N; ++i) cin >> x[i] >> y[i];

  for (int i = 0; i < N; ++i) {
    for (int j = i + 1; j < N; ++j) {
      hoge(i, j);
    }
  }

  UnionFind start(N);
  vector<pair<UnionFind, int>> dp;
  dp.push_back({start, 0});
  int mi = N;
  for (int i = 0; i < N; ++i) {
    for (int j = i + 1; j < N; ++j) {
      vector<pair<UnionFind, int>> v;
      for (auto& p : dp) {
        if (p.first.findSet(i, j)) continue;
        UnionFind next(p.first);
        for (auto& q : m[{i, j}]) {
          next.unionSet(q.first, q.second);
        }
        for (int k = 1; k < N; ++k) {
          if (!next.findSet(0, k)) {
            v.push_back({next, p.second + 1});
            break;
          }
          if (k == N - 1) chmin(mi, p.second + 1);
        }
      }
      for (auto& q : v) {
        dp.push_back(q);
      }
    }
  }
  cout << mi << endl;
}
accuracy: 1
time_ms: 110
memory_kb: 48364
final_rank: 14

code_snippet: /*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
             .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            pass System Test!
*/
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
  if (!v.empty()) {
    out << '[';
    std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
    out << "\b\b]";
  }
  return out;
}
template <class T, class U>
void chmin(T& t, U f) {
  if (t > f) t = f;
}
template <class T, class U>
void chmax(T& t, U f) {
  if (t < f) t = f;
}

struct UnionFind {
  vector<int> data;
  UnionFind(int size) : data(size, -1) {}
  UnionFind(const UnionFind& u) { data = u.data; }
  bool unionSet(int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) {
      if (data[y] < data[x]) swap(x, y);
      data[x] += data[y];
      data[y] = x;
    }
    return x != y;
  }
  bool findSet(int x, int y) { return root(x) == root(y); }
  int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
  int size(int x) { return -data[root(x)]; }
};

int N;
vector<int> x, y;
map<pair<int, int>, vector<pair<int, int>>> m;

void hoge(int i, int j) {
  //-2x(x2-x1)-2y(y2-y1)+()+()
  ll a = -2 * (x[j] - x[i]);
  ll b = -2 * (y[j] - y[i]);
  ll c = (x[j] * x[j] - x[i] * x[i]) + (y[j] * y[j] - y[i] * y[i]);
  for (int k = 0; k < N; ++k) {
    for (int l = k + 1; l < N; ++l) {
      if (abs(a * x[l] + b * y[l] + c) == abs(a * x[k] + b * y[k] + c) &&
          (x[k] - x[l]) * (y[i] - y[j]) == (y[k] - y[l]) * (x[i] - x[j])) {
        m[{i, j}].push_back({k, l});
      }
    }
  }
}

int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  cin >> N;
  x.resize(N), y.resize(N);
  for (int i = 0; i < N; ++i) cin >> x[i] >> y[i];

  for (int i = 0; i < N; ++i) {
    for (int j = i + 1; j < N; ++j) {
      hoge(i, j);
    }
  }

  UnionFind start(N);
  vector<pair<UnionFind, int>> dp;
  dp.push_back({start, 0});
  int mi = N;
  for (int i = 0; i < N; ++i) {
    for (int j = i + 1; j < N; ++j) {
      vector<pair<UnionFind, int>> v;
      for (auto& p : dp) {
        if (p.first.findSet(i, j)) continue;
        UnionFind next(p.first);
        for (auto& q : m[{i, j}]) {
          next.unionSet(q.first, q.second);
        }
        for (int k = 1; k < N; ++k) {
          if (!next.findSet(0, k)) {
            v.push_back({next, p.second + 1});
            break;
          }
          if (k == N - 1) chmin(mi, p.second + 1);
        }
      }
      for (auto& q : v) {
        dp.push_back(q);
      }
    }
  }
  cout << mi << endl;
}
accuracy: 1
time_ms: 110
memory_kb: 48448
final_rank: 15

code_snippet: #include<bits/stdc++.h>
using namespace std;

#define int long long

typedef long long ll;
typedef pair<int,int>pint;
typedef vector<int>vint;
typedef vector<pint>vpint;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define all(v) (v).begin(),(v).end()
#define rep(i,n) for(int i=0;i<(n);i++)
#define reps(i,f,n) for(int i=(f);i<(n);i++)
#define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++)
template<class T,class U>void chmin(T &t,U f){if(t>f)t=f;}
template<class T,class U>void chmax(T &t,U f){if(t<f)t=f;}

int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}

struct data{
    int vx,vy,x0,y0;
    data(int a,int b,int c,int d){
        int g=gcd(abs(a),abs(b));
        a/=g;b/=g;
        if(a<0)a*=-1,b*=-1;
        else if(a==0&&b<0)b*=-1;
        vx=a;vy=b;x0=c;y0=d;
    }
    bool operator==(const data &d){
        if(vx!=d.vx||vy!=d.vy)return false;
        int xx=x0-d.x0,yy=y0-d.y0;
        if(vx==0)return xx==0;
        if(vy==0)return yy==0;
        return xx%vx==0&&yy%vy==0&&xx/vx==yy/vy;
    }
};

int N;
int x[10],y[10];

int table[10][10];

int p[]={1,2,3,4,5,6,7,8};
vint lis;
bool f[100];
int ans=1145141919;

void dfs(int n,int c){
    if(n==N-1){
        chmin(ans,c);
        return;
    }

    int idx=p[n];
    rep(i,lis.size()){
        int tmp=table[lis[i]][idx];
        lis.pb(idx);
        if(f[tmp])dfs(n+1,c);
        else{
            f[tmp]=1;
            dfs(n+1,c+1);
            f[tmp]=0;
        }
        lis.pop_back();
    }
}

signed main(){
    cin>>N;
    rep(i,N)cin>>x[i]>>y[i],x[i]*=2,y[i]*=2;

    vector<data>vec;
    rep(i,N)rep(j,N)if(i!=j){
        data d(y[j]-y[i],x[i]-x[j],(x[i]+x[j])/2,(y[i]+y[j])/2);
        int t=-1;
        rep(k,vec.size())if(vec[k]==d)t=k;
        if(t==-1){
            table[i][j]=vec.size();
            vec.pb(d);
        }
        else{
            table[i][j]=t;
        }
    }

    do{
        lis.clear();
        lis.pb(0);
        dfs(0,0);
    }while(next_permutation(p,p+N-1));

    cout<<ans<<endl;

    return 0;
}
accuracy: 1
time_ms: 210
memory_kb: 1220
final_rank: 8

code_snippet: #include <bits/stdc++.h>

using namespace std;

#define FOR(i,n) for(int i = 0 ;i < (n); i++) 
#define sz(c) ((int)c.size())

class UF {
public:
	int n; vector<int> a;
	UF(int n) : n(n), a(n, -1) {}
	int find(int x) {
		return a[x] < 0 ? x : (a[x] = find(a[x]));
	}
	bool unite(int x, int y) {
		x = find(x), y = find(y);
		if (x == y) return false;
		if (a[x] > a[y]) swap(x, y);
		a[x] += a[y];
		a[y] = x;
		n--;
		return true;
	}
	int size(int x) {
		return -a[find(x)];
	}
};

struct P {
	P() {}
	int x, y;
	P(int x, int y) : x(x), y(y) {}
	P operator+(const P& r) const {
		return P(x + r.x, y + r.y);
	}
	P operator-(const P& r) const {
		return P(x - r.x, y - r.y);
	}
};

int cross(P& l, P& r) {
	return l.x * r.y - l.y * r.x;
}
bool on(P a, P b, P c) {
	b = b - a;
	c = c - a;
	return cross(b, c) == 0;
}

P center(P& l, P& r) {
	return P((l.x + r.x) / 2, (l.y + r.y) / 2);
}

struct L {
	P o; P dir;
	L(){};
};

L l[8][8];
bool b[8][8][8][8];

map<vector<int>, int> mp;
int dfs(vector<int> f) {
	if (mp.count(f)) return mp[f];

	bool end = true;
	FOR(i, sz(f)) {
		if (f[i] != 0) end = false;
	}
	if (end) {
		return mp[f] = 0;
	}

	int ans = 100;
	FOR(i, sz(f)) {
		FOR(j, i) {
			if (f[i] == f[j]) continue;
			auto copyed = f;
			FOR(a, sz(f)) FOR(b, a) {
				if (f[b] == f[a]) continue;
				bool er = ::b[i][j][a][b];
				if (er) {
					int to = f[b];
					int from = f[a];
					FOR(c, sz(f)) if (f[c] == from) f[c] = to;
				}
			}
			ans = min(ans,dfs(f));
			f = copyed;
		}
	}

	return mp[f] = ans + 1;
}

int main() {
	int n; cin >> n;
	vector<P> vp;
	FOR(i, n) {
		int x, y; cin >> x >> y; vp.emplace_back(x * 2, y * 2);
	}
	FOR(i, n) FOR(j, n) {
		if (i == j) continue;
		l[i][j].o = center(vp[i], vp[j]);
		auto d = vp[i] - vp[j];
		l[i][j].dir = P(-d.y, d.x);
	}
	FOR(i, n) FOR(j, n) {
		if (i == j) continue;
		FOR(a, n) FOR(b, n) {
			if (a == b) continue;
			bool x = on(l[i][j].o, l[i][j].o + l[i][j].dir, l[a][b].o);
			bool y = on(l[i][j].o, l[i][j].o + l[i][j].dir, l[a][b].o + l[a][b].dir);
			::b[i][j][a][b] = x && y;
		}
	}

	vector<int> p(n);
	FOR(i, n) p[i] = i;

	int ans = dfs(p);
	cout << ans << endl;
}
accuracy: 1
time_ms: 80
memory_kb: 4560
final_rank: 5

code_snippet: #include <bits/stdc++.h>

using namespace std;

typedef long double ld;
typedef complex<ld> Point;
typedef pair<int, int> P;

const ld eps = 1e-12, pi = acos(-1.0);

const int inf = 1e9;

namespace std {
	bool operator<(const Point& lhs, const Point& rhs) {
		if (lhs.real() < rhs.real() - eps) return true;
		if (lhs.real() > rhs.real() + eps) return false;
		return lhs.imag() < rhs.imag();
	}
}

struct Line {
	Point a, b;
	Line() : a(Point(0,0)), b(Point(0,0)) {}
	Line(Point a, Point b) : a(a), b(b) {}
};

struct Edge {
	int from, to, id;
};

int N;
map<P, int> tbl2;
map<int, int> tbl;
vector< vector<Edge> > G;
vector<Edge> edges;

int to_hash(vector<int> v) {
	int res = 0;
	for (int i = 0; i < N; ++i) {
		res = res * 5 + v[i];
	}
	return res;
}
vector<int> from_hash(int hash) {
	vector<int> res;
	for (int i = 0; i < N; ++i) {
		res.push_back(hash%5);
		hash /= 5;
	}
	reverse( res.begin(), res.end() );
	return res;
}

class UnionFind {
private:
	int sz;
	vector<int> par, h;

public:
	UnionFind(int size) : sz(size) {
		par.resize(sz);
		for (int i = 0; i < sz; ++i) {
			par[i] = i;
		}
		h = vector<int>(sz, 0);
	}
	int root(int n) {
		if (par[n] == n) return n;
		return par[n] = root(par[n]);
	}
	bool isUnited(int a, int b) {
		return root(a) == root(b);
	}
	void unite(int a, int b) {
		int ra = root(a), rb = root(b);
		if (ra == rb) return;
		if (h[ra] < h[rb]) {
			par[ra] = rb;
		}
		else if (h[ra] > h[rb]) {
			par[rb] = ra;
		}
		else {
			par[rb] = ra;
			++h[ra], ++h[rb];
		}
	}
	int hash() {
		vector<int> ids(sz);
		for (int i = 0; i < sz; ++i) {
			int id = root(i);
			if ( h[id] == 0 ) {
				id = 0;
			}
			else {
				++id;
			}
			ids[i] = id;
		}
		map<int, int> m; m[0] = 0;
		for (int i = 0, k = 1; i < ids.size(); ++i) {
			if (m.count(ids[i]) == 0) {
				m[ids[i]] = k++;
			}
		}

		for (int i = 0; i < ids.size(); ++i) {
			ids[i] = m[ids[i]];
		}

		assert( ids == from_hash(to_hash(ids)) );
		return to_hash(ids);
	}
};

int get_id(P p) {
	if ( tbl2.count(p) == 0 ) {
		int id = tbl2.size();
		tbl2[p] = id;
	}
	return tbl2[p];
}
int gcd(int a, int b) {
	if (b == 0) return a;
	return gcd(b, a%b);
}
void add_edge(int a, int b, int id) {
//	cout << "add_edge:" << a << " " << b << " " << id << endl;
	G[a].push_back( (Edge){a, b, id} );
	G[b].push_back( (Edge){b, a, id} );
	edges.push_back( (Edge){a, b, id} );
}
struct Data {
	Line l;
	int lid;
	int i, j;
};

ld cross(Point a, Point b) {
	return imag(conj(a) * b);
}
bool isis_lp(Line l, Point p) {
	return (abs(cross(l.b-p, l.a-p)) < eps);
}
void input_and_make_graph() {
	cin >> N;
	G.resize(N);
	vector<Point> v;
	for (int i = 0; i < N; ++i) {
		int x, y; cin >> x >> y;
		v.push_back( Point(x, y) );
	}
	vector<Data> dt;
	for (int i = 0; i < N; ++i) {
		for (int j = i+1; j < N; ++j) {
			Point p1 = v[i], p2 = v[j];
			Point m = (p1 + p2) / Point(2, 0);
			Point a = (p2 - p1) * Point(0, 1);
			int vx = a.real(), vy = a.imag();
			int g = gcd(abs(vx), abs(vy)); vx /= g, vy /= g;
			if ((vx < 0) || (vx == 0 && vy < 0)) {
				vx *= -1, vy *= -1;
			}
			int id = get_id( P(vx, vy) );
			Line l(m, m+Point(vx, vy));
			dt.push_back( (Data){l, id, i, j} );
		}
	}
	UnionFind uf(dt.size());
	for (int i = 0; i < dt.size(); ++i) {
		for (int j = i+1; j < dt.size(); ++j) {
			if ( dt[i].lid == dt[j].lid && isis_lp(dt[j].l, dt[i].l.a) ) {
				uf.unite(i, j);
			}
		}
	}
	vector<int> ids(dt.size());
	for (int i = 0; i < dt.size(); ++i) {
		ids[i] = uf.root(i);
	}
	{
		vector<int> temp = ids;
		sort( temp.begin(), temp.end() );
		temp.erase( unique(temp.begin(), temp.end()), temp.end() );
		for (int i = 0; i < temp.size(); ++i) {
			tbl[temp[i]] = i;
//			cout << "-" << i << endl;
		}
	}
	for (int i = 0; i < dt.size(); ++i) {
		add_edge(dt[i].i, dt[i].j, tbl[ids[i]]);
	}
//			add_edge(i, j, edge_id);
}

int dp[2][400000];
vector< vector<int> > bucket(vector<int> v) {
	vector< vector<int> > res(N+1);
	for (int i = 0; i < v.size(); ++i) {
		res[v[i]].push_back(i);
	}
	return res;
}
int merge(int h1, int h2) {
//	cout << "merge" << endl;
/*
	{
		vector<int> v = from_hash(h1);
		cout << "v1:"; for (int i = 0; i < v.size(); ++i) cout << v[i] << " ";
		cout << endl;
	}
	{
		vector<int> v = from_hash(h2);
		cout << "v2:"; for (int i = 0; i < v.size(); ++i) cout << v[i] << " ";
		cout << endl;
	}
*/
	vector< vector<int> > v1 = bucket(from_hash(h1));
//	cout << "v1" << endl;
	vector< vector<int> > v2 = bucket(from_hash(h2));
	UnionFind uf(N);
//	cout << "bucket" << endl;
	for (int i = 1; i < v1.size(); ++i) {
		assert( v1[i].size() == 0 || v1[i].size() >= 2 );
		for (int j = 1; j < v1[i].size(); ++j) {
//			cout << v1[i][j-1] << v1[i][j] << endl;
			uf.unite(v1[i][j], v1[i][j-1]);
		}
	}
//	cout << "v2" << endl;
	for (int i = 1; i < v2.size(); ++i) {
		assert( v2[i].size() == 0 || v2[i].size() >= 2 );
		for (int j = 1; j < v2[i].size(); ++j) {
//			cout << v2[i][j-1] << v2[i][j] << endl;
			uf.unite(v2[i][j], v2[i][j-1]);
		}
	}
//	cout << "merge_r" << endl;
	return uf.hash();
}
int solve() {
	vector<int> h(tbl.size());
	{
		vector< vector<Edge> > v(tbl.size());
		for (int i = 0; i < edges.size(); ++i) {
			Edge& e = edges[i];
			v[e.id].push_back(e);
		}
		for (int i = 0; i < v.size(); ++i) {
			UnionFind uf(N);
			for (int j = 0; j < v[i].size(); ++j) {
				Edge& e = v[i][j];
				uf.unite(e.from, e.to);
			}
			h[i] = uf.hash();
		}
	}

	int maxJ = 1;
	for (int i = 0; i < N; ++i) maxJ *= 5;
	--maxJ;

	fill(dp[0], dp[2], inf); dp[0][0] = 0;
	for (int i = 0; i < h.size(); ++i) {
		fill(&dp[i+1&1][0], &dp[i+1&1][400000], inf);
		for (int j = 0; j <= maxJ; ++j) {
			if (dp[i&1][j] == inf) continue;
			dp[i+1&1][j] = min(dp[i+1&1][j], dp[i&1][j]);

//			cout << "!" << i << " " << j << " " << dp[i&1][j] << endl;

			int nj = merge(j, h[i]);
			dp[i+1&1][nj] = min(dp[i+1&1][nj], dp[i&1][j]+1);
//			cout << "h:" << j << " " << h[i] << " " << nj << endl;
		}
	}
	vector<int> a;
	for (int i = 0; i < N; ++i) a.push_back(1);
	return dp[((int)h.size())&1][to_hash(a)];
}
int main() {
	input_and_make_graph();
	cout << solve() << endl;
}
accuracy: 1
time_ms: 50
memory_kb: 6304
final_rank: 6

code_snippet: #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int,int> pii;
#define REP(i,n) for(ll i=0;i<n;++i)
#define REPR(i,n) for(ll i=1;i<n;++i)
#define FOR(i,a,b) for(ll i=a;i<b;++i)

#define DEBUG(x) cout<<#x<<": "<<x<<endl
#define DEBUG_VEC(v) cout<<#v<<":";REP(i,v.size())cout<<" "<<v[i];cout<<endl
#define ALL(a) (a).begin(),(a).end()

#define MOD (ll)(1e9+7)
#define ADD(a,b) a=((a)+(b))%MOD
#define FIX(a) ((a)%MOD+MOD)%MOD

#define N_MAX 8

// int dat[N_MAX];
void init(vi &dat){
  dat.assign(N_MAX,-1);
}
int root(vi &dat,int x){
  return dat[x]<0 ? x : (dat[x]=root(dat,dat[x]));
}
void unite(vi &dat,int x,int y){
  x=root(dat,x);
  y=root(dat,y);
  if(x!=y){
    if(dat[x]>dat[y])swap(x,y);
    dat[x] += dat[y];
    dat[y] = x;
  }
}
bool same(vi &dat, int x,int y){
  return root(dat,x)==root(dat,y);
}
int size(vi &dat, int x){
  return -dat[root(dat,x)];
}

typedef double Real;
typedef complex<Real> P;
Real dot(P a,P b){return real(conj(a)*b);}
Real cross(P a,P b){return imag(conj(a)*b);}
#define EPS 1e-14

int n;
P p[10];
vector<pii> connect[10][10];

int dfs(const int &cnt,vi &uf){
  if(size(uf,0) == n)return cnt;

  int ret = 100;
  REP(to,n){
    if(same(uf,0,to))continue;
    REP(from,n){
      if(!same(uf,0,from))continue;
      vector<pii> con = connect[from][to];
      vi nextuf = uf;
      REP(i,con.size()) unite(nextuf,con[i].first,con[i].second);
      int d = dfs(cnt+1,nextuf);
      if(d==cnt+1)return d;
      ret = min(ret,d);
    }
  }
  return ret;
}

int main(){
  cin>>n;
  REP(i,n){
    int x,y;
    cin>>x>>y;
    p[i] = P((Real)x,(Real)y);
  }
  REP(i,n)REP(j,n){
    if(i==j)continue;
    // make connect[i][j];
    P v = p[i]-p[j];
    P cv = p[j] + v/P(2,0);
    P mirror = v*P(0,1);
    REP(x,n){
      P a = p[x];
      // reflect
      a = conj((a-cv)/mirror)*mirror+cv;
      REP(y,x){
        if(abs(p[y]-a)<EPS)connect[i][j].push_back(make_pair<int,int>(x,y));
      }
    }
  }
  vi beg;
  init(beg);
  cout<<dfs(0,beg)<<endl;
  return 0;
}
accuracy: 1
time_ms: 870
memory_kb: 1240
final_rank: 13

code_snippet: #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int,int> pii;
#define REP(i,n) for(ll i=0;i<n;++i)
#define REPR(i,n) for(ll i=1;i<n;++i)
#define FOR(i,a,b) for(ll i=a;i<b;++i)

#define DEBUG(x) cout<<#x<<": "<<x<<endl
#define DEBUG_VEC(v) cout<<#v<<":";REP(i,v.size())cout<<" "<<v[i];cout<<endl
#define ALL(a) (a).begin(),(a).end()

#define MOD (ll)(1e9+7)
#define ADD(a,b) a=((a)+(b))%MOD
#define FIX(a) ((a)%MOD+MOD)%MOD

#define N_MAX 8
int g[N_MAX][N_MAX];
void init(){
  REP(i,N_MAX)REP(j,N_MAX)g[i][j]=0;
  REP(i,N_MAX)g[i][i]=(rand()%2?252521:83025);
}
void unite(int x,int y){
  g[x][y] += 1;
  g[y][x] += 1;
}
void haki(int x,int y){
  g[x][y] -= 1;
  g[y][x] -= 1;
}

typedef double Real;
typedef complex<Real> P;
Real dot(P a,P b){return real(conj(a)*b);}
Real cross(P a,P b){return imag(conj(a)*b);}
#define EPS 1e-14

int n;
P p[10];
vector<pii> connect[10][10];

int dfs(const int &pos,const int &cnt){
  //////////
  bool h[N_MAX][N_MAX];
  REP(i,N_MAX)REP(j,N_MAX)h[i][j]=(g[i][j]>0);
  REP(k,N_MAX)REP(i,N_MAX)REP(j,N_MAX)h[i][j] |= h[i][k]&&h[k][j];
  //////////
  int sz = 0;
  REP(i,n)sz += h[pos][i];

  if(sz == n)return cnt;

  int ret = 100;
  REP(to,n){
    if(h[pos][to])continue;
    vector<pii> con = connect[pos][to];
    REP(i,con.size()) unite(con[i].first,con[i].second);
    ret = min(ret,dfs(to,cnt+1));
    REP(i,con.size()) haki(con[i].first,con[i].second);
  }
  return ret;
}

int main(){
  cin>>n;
  REP(i,n){
    int x,y;
    cin>>x>>y;
    p[i] = P((Real)x,(Real)y);
  }
  REP(i,n)REP(j,n){
    if(i==j)continue;
    // make connect[i][j];
    P v = p[i]-p[j];
    P cv = p[j] + v/P(2,0);
    P mirror = v*P(0,1);
    REP(x,n){
      P a = p[x];
      // reflect
      a = conj((a-cv)/mirror)*mirror+cv;
      REP(y,x){
        if(abs(p[y]-a)<EPS)connect[i][j].push_back(make_pair<int,int>(x,y));
      }
    }
    // REP(x,n)REP(y,x){
    //   P w = p[x]-p[y];
    //   if(abs(cross(v,w))<EPS){
    //     P cw = p[y] + w/P(2,0);
    //     if( (abs(cv-cw)<EPS)||(abs(dot(cw-cv,v))<EPS) ){
    //       connect[i][j].push_back(make_pair<int,int>(x,y));
    //     }
    //   }
    // }
  }
  init();
  cout<<dfs(0,0)<<endl;
  return 0;
}
accuracy: 0.48484848484848486
time_ms: 10
memory_kb: 1228
final_rank: 16

code_snippet: #include<bits/stdc++.h>
#define EQ(a,b) (abs((a)-(b)) < EPS)
using namespace std;
typedef double D;
typedef complex<D> P;
typedef pair<P,P> L;
typedef pair<int,int> pii;
typedef vector<pii> vp;

const D EPS = 1e-8;
const D PI = acos(-1);

void normalize(vector<int> &v){
  map<int,int> cnt;
  int k = 0;
  for(int i=0;i<(int)v.size();i++){
    if(cnt.count(v[i]) == 0){
      cnt[v[i]] = k++;
    }
  }
 
  for(int i=0;i<(int)v.size();i++){
    v[i] = cnt[v[i]];
  }
}

inline D dot(P a, P b){
  return real(conj(a)*b);
}

inline D cross(P a, P b){
  return imag(conj(a)*b);
}

inline int ccw(P a, P b, P c){
  b -= a; c-= a;
  if(cross(b,c)>EPS) return 1;
  if(cross(b,c)<-EPS) return -1;
  return 0;
}

inline P rotate(P v, D s){
  return P(real(v)*cos(s) - imag(v)*sin(s), real(v)*sin(s) + imag(v)*cos(s) );
}

inline P norm(P p){return p*P(0,1);}

map< pair<int, vector<int> >, int> memo;

int rec(int d, vector<int> use, vector<vp> &segs){
  pair< int, vector<int> > key(d,use);
  if(memo.count(key)) return memo[key];

  if(d == (int)segs.size()){
    /*
    for(int i=0;i<(int)use.size();i++){
      cout << use[i] << " ";
    }
    cout << endl;
    */

    set<int> c;
    for(int x : use) c.insert(x);

    if(c.size() == 1) return 0;
    else return 100;
  }

  int res = rec(d+1,use,segs);

  for(auto p : segs[d]){
    int a = use[p.first], b = use[p.second];
    for(int i=0;i<(int)use.size();i++){
      if(use[i] == a) use[i] = b;
    }
  }
  normalize(use);
  res = min(res, rec(d+1,use,segs) + 1);

  return memo[key] = res;
}

int main(){
  int n;
  cin >> n;

  vector<P> p(n);
  for(int i=0;i<n;i++){
    int x,y;
    cin >> x >> y;
    p[i] = P(x,y);
  }

  vector< vp > segs;
  for(int i=0;i<n;i++){
    for(int j=i+1;j<n;j++){
      P mid = (p[i]+p[j])/2.0;
      P nv = norm(p[i] - p[j]);
      L seg = make_pair(mid, mid + nv);

      vp ans;
      for(int k=0;k<n;k++){
	for(int l=k+1;l<n;l++){
	  P mid_c = (p[k]+p[l])/2.0;
	  if( ccw(seg.first, seg.second, mid_c) == 0 ){
	    if( abs( dot(seg.second - seg.first, p[k] - p[l])) < EPS){
	      ans.push_back( make_pair(k,l) );
	    }
	  }
	}
      }
      /*
      cout << i << " " << j << ": " << endl;
      for(pii a : ans){
	cout << a.first << "," << a.second << " ";
      }
      cout << endl;
      */

      segs.push_back(ans);
    }
  }

  vector<int> use(n,0);
  for(int i=0;i<n;i++)use[i] = i;

  cout << rec(0,use,segs) << endl;
}
accuracy: 1
time_ms: 50
memory_kb: 7576
final_rank: 7