Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "trace.h"
- #include "timer.h"
- #include <string>
- #include <vector>
- #include <cassert>
- #include <sstream>
- #include <algorithm>
- #include <numeric>
- #include <unordered_set>
- #include <set>
- #include <time.h>
- #include <random>
- // These three constants are adjustable
- const bool DO_MAX = true;
- const int N_ = 17;
- const int MARGIN = 1; // how much half-area away from theoretical optimal will you tolerate?
- const int HULL_SIZE = 4; // used for "max" only
- const int N = N_ - ( DO_MAX ? HULL_SIZE : 0 );
- static int g_BEST = (DO_MAX ? HULL_SIZE == 3 ? N-1 : N : N-2 ) + MARGIN;
- using namespace std;
- static string searchName()
- {
- stringstream ss;
- ss << N_;
- ss << (DO_MAX ? "max" : "min");
- if ( DO_MAX )
- ss << " hull" << HULL_SIZE;
- return ss.str();
- }
- class XY
- {
- public:
- XY( int px, int py ) { set( px, py ); }
- XY() { set(0, 0); }
- ~XY() {}
- void set( int px, int py ) { x = px; y = py; }
- int len2() const { return x*x + y*y; }
- int dist2( const XY& p ) const { return (p.x-x)*(p.x-x) + (p.y-y)*(p.y-y); }
- XY transposed() const { return XY( y, x ); }
- XY operator-() const { return XY( -x, -y ); }
- XY operator-( const XY& p ) const { return XY( x - p.x, y - p.y ); }
- XY operator+( const XY& p ) const { return XY( x + p.x, y + p.y ); }
- XY operator*( int iMul ) const { return XY(x * iMul, y * iMul); }
- XY operator/( int iDiv ) const { return XY(x / iDiv, y / iDiv); }
- const XY& operator-=( const XY& p ) { return *this = *this - p; }
- const XY& operator+=( const XY& p ) { return *this = *this + p; }
- const XY& operator*=( int iMul ) { return *this = *this * iMul; }
- const XY& operator/=( int iDiv ) { return *this = *this / iDiv; }
- int operator^( const XY& p ) const { return x*p.y - y*p.x; }
- int operator*( const XY& p ) const { return x*p.x + y*p.y; }
- bool operator==( const XY& p ) const { return x == p.x && y == p.y; }
- bool operator!=( const XY& p ) const { return !(*this == p); }
- bool operator<( const XY& p ) const { return (y != p.y) ? (y < p.y) : (x < p.x); }
- static XY dir( int x ) { static XY a[] = { XY(1,0), XY(0,1), XY(-1,0), XY(0,-1) }; return a[x]; }
- public:
- int x, y;
- };
- int doubleArea( const vector<XY>& v )
- {
- int ret = v.empty() ? 0 : v.back()^v[0];
- for ( int i = 0; i+1 < (int) v.size(); i++ )
- ret += v[i]^v[i+1];
- return abs(ret);
- }
- bool intersects( int a, int b, int u, int v )
- {
- return max( a, b ) >= min( u, v )
- && min( a, b ) <= max( u, v );
- }
- // assumes different slopes
- bool intersects( const XY& a, const XY& b, const XY& u, const XY& v )
- {
- //int num0 = (u-a)^(v-u);
- //int den0 = (b-a)^(v-u);
- //int num1 = (a-u)^(b-a);
- //int den1 = (v-u)^(b-a);
- //if ( den0 < 0 ) { num0 = -num0; den0 = -den0; }
- //if ( den1 < 0 ) { num1 = -num1; den1 = -den1; }
- //return num0 >= 0 && num0 <= den0
- // && num1 >= 0 && num1 <= den1;
- assert( a != b );
- assert( u != v );
- int num0 = (u-a)^(v-u);
- int den0 = (b-a)^(v-u);
- int num1 = (u-a)^(b-a);
- if ( den0 == 0 ) // same slope
- {
- //return false;
- if ( num0 != 0 ) return false; // parallel, but not colinear
- return a.x != b.x ? intersects( a.x, b.x, u.x, v.x ) : intersects( a.y, b.y, u.y, v.y );
- }
- if ( den0 < 0 ) { num0 = -num0; num1 = -num1; den0 = -den0; }
- return num0 >= 0 && num0 <= den0
- && num1 >= 0 && num1 <= den0;
- }
- bool isClockwise( const XY& a, const XY& b, const XY& c ) { return ((a-b)^(c-b)) < 0; }
- bool isClockwiseOrColinear( const XY& a, const XY& b, const XY& c ) { return ((a-b)^(c-b)) <= 0; }
- bool isInOrOnClockwiseTriangle( const XY& a, const XY& b, const XY& c, const XY& p )
- {
- return isClockwiseOrColinear( a,b,p )
- && isClockwiseOrColinear( b,c,p )
- && isClockwiseOrColinear( c,a,p );
- }
- int gcd( int a, int b ) { return a ? gcd( b%a, a ) : b; }
- int gcd( const XY& pt ) { return gcd( abs(pt.x), abs(pt.y) ); }
- string str( const XY& p )
- {
- stringstream ss;
- ss << "(" << p.x << "," << p.y << ")";
- return ss.str();
- }
- string str( const vector<XY>& v, int offset )
- {
- stringstream ss;
- for ( int i = 0; i < (int) v.size(); i++ )
- ss << (i ? "," : "") << str( v[i] + XY(offset,offset) );
- return ss.str();
- }
- template<int N>
- class SlopeInfo
- {
- public:
- SlopeInfo()
- {
- memset( m, -1, sizeof( m ) );
- m[0][0] = 0;
- XY p;
- for ( p.y = 0; p.y < N; p.y++ )
- for ( p.x = 0; p.x < N; p.x++ )
- {
- if ( m[p.y][p.x] >= 0 ) continue;
- for ( XY q = p; q.x < N && q.y < N; q += p )
- m[q.y][q.x] = _NumIds;
- _NumIds++;
- }
- _NumSlopes = _NumIds * 2 - 2;
- }
- static const SlopeInfo& TheInstance()
- {
- static SlopeInfo s_theInstance;
- return s_theInstance;
- }
- int IdOf_( XY p ) const
- {
- if ( p.y == 0 ) return 0;
- if ( p.y < 0 ) p = -p;
- return p.x > 0 ? m[p.y][p.x] : m[p.y][-p.x] + _NumIds-2;
- }
- static int IdOf( const XY& p )
- {
- return TheInstance().IdOf_( p );
- }
- static int NumUniqueSlopes() { return TheInstance()._NumSlopes; }
- public:
- int m[N][N];
- int _NumIds;
- int _NumSlopes;
- };
- template<int N>
- class SlopeInfoSym
- {
- public:
- SlopeInfoSym()
- {
- memset( m, -1, sizeof( m ) );
- m[0][0] = 0;
- XY p;
- for ( p.y = 0; p.y < N; p.y++ )
- for ( p.x = 0; p.x < N; p.x++ )
- {
- if ( m[p.y][p.x] >= 0 ) continue;
- for ( XY q = p; q.x < N && q.y < N; q += p )
- m[q.y][q.x] = m[q.x][q.y] = _NumIds;
- _NumIds++;
- }
- _NumSlopes = _NumIds * 2 - 1;
- }
- static const SlopeInfoSym& TheInstance()
- {
- static SlopeInfoSym s_theInstance;
- return s_theInstance;
- }
- int IdOf_( XY p ) const
- {
- if ( p.y == 0 ) return 0;
- if ( p.y < 0 ) p = -p;
- return p.x > 0 ? m[p.y][p.x] : m[p.y][-p.x] + _NumIds-1;
- }
- static int IdOf( XY p )
- {
- return TheInstance().IdOf_( p );
- }
- static int NumUniqueSlopes() { return TheInstance()._NumSlopes; }
- public:
- int m[N][N];
- int _NumIds;
- int _NumSlopes;
- };
- struct LatticeLineSegment
- {
- XY pt0;
- XY dir;
- int n;
- XY pt( int i ) const { return pt0 + dir * i; }
- std::vector<XY> allPoints() const { std::vector<XY> v; for ( int i = 0; i < n; i++ ) v.push_back( pt( i ) ); return v; }
- std::set<XY> allPointsSet() const { std::vector<XY> v = allPoints(); return std::set<XY>( v.begin(), v.end() ); }
- static LatticeLineSegment emptySet() { return LatticeLineSegment{ XY(), XY(), 0 }; }
- };
- // ax + by = c
- LatticeLineSegment findLatticePointsOnLine( int a, int b, int c, int lo, int hi )
- {
- assert( a != 0 && b != 0 );
- if ( abs( gcd( a, b ) ) != 1 )
- return LatticeLineSegment::emptySet();
- // ax + by = c (mod b)
- // ax = c (mod b)
- XY pt0;
- for ( pt0.x = lo; ( c - a * pt0.x ) % b; pt0.x++ );
- pt0.y = (c - a * pt0.x) / b;
- XY dir = XY( b, -a );
- // the line: pt0 + dir * t
- // on x-axis: lo <= pt0.x + dir.x * t pt0.x + dir.x * t < hi
- // (lo - pt0.x) / dir.x <= t t < (hi - pt0.x) / dir.x (for reals) (for positive dir.x)
- // (lo - pt0.x + dir.x-1) / dir.x <= t t < (hi - pt0.x + dir.x - 1) / dir.x (for integers)
- // (lo - pt0.x) / dir.x >= t t > (hi - pt0.x) / dir.x (for reals) (for negative dir.x)
- // (lo - pt0.x) / dir.x >= t t > (hi - pt0.x) / dir.x (for integers)
- // (hi - pt0.x) / dir.x < t t <= (lo - pt0.x) / dir.x (for integers)
- // (hi - pt0.x) / dir.x + 1 <= t t < (lo - pt0.x) / dir.x + 1 (for integers)
- // 1024 to make division always do floor
- int tlo_x = ( dir.x >= 0 ? (lo - pt0.x + dir.x-1 + dir.x*1024) / dir.x : (hi - pt0.x + dir.x*1024) / dir.x + 1 ) - 1024;
- int thi_x = ( dir.x >= 0 ? (hi - pt0.x + dir.x-1 + dir.x*1024) / dir.x : (lo - pt0.x + dir.x*1024) / dir.x + 1 ) - 1024;
- int tlo_y = ( dir.y >= 0 ? (lo - pt0.y + dir.y-1 + dir.y*1024) / dir.y : (hi - pt0.y + dir.y*1024) / dir.y + 1 ) - 1024;
- int thi_y = ( dir.y >= 0 ? (hi - pt0.y + dir.y-1 + dir.y*1024) / dir.y : (lo - pt0.y + dir.y*1024) / dir.y + 1 ) - 1024;
- int tlo = max( tlo_x, tlo_y );
- int thi = min( thi_x, thi_y );
- return { pt0 + dir*tlo, dir, max( thi - tlo, 0 ) };
- }
- namespace Shards
- {
- class Poly
- {
- public:
- Poly()
- {
- _NumCommittedEdges = 0;
- _UsedX = 0;
- _UsedY = 0;
- _DoubledArea = 0;
- memset( _UsedSlope, 0, sizeof( _UsedSlope ) );
- }
- int NewAreaFor( const XY& pt ) const { return v.size() >= 3 ? abs((v[_NumCommittedEdges]-pt)^(v[_NumCommittedEdges+1]-pt)) : 0; }
- int MinimumAreaWith( const XY& pt ) const { return _DoubledArea + NewAreaFor( pt ) + (N-1-NumPoints()); }
- int MinimumArea() const { return _DoubledArea + (N-NumPoints()); }
- bool IsSideways( const XY& pt ) const
- {
- bool s0 = (pt.x > v[_NumCommittedEdges].x) != (pt.y > v[_NumCommittedEdges].y);
- bool s1 = (pt.x > v[_NumCommittedEdges+1].x) != (pt.y > v[_NumCommittedEdges+1].y);
- return s0 && s1;
- }
- bool IsClosed() const { return _NumCommittedEdges + 1 >= (int) v.size() && (int) v.size() > 3; }
- bool AddPoint( const XY& pt )
- {
- if ( IsClosed() )
- return false; // all edges are committed, can't add anything more!
- if ( _UsedX & (1LL << pt.x) ) return false;
- if ( _UsedY & (1LL << pt.y) ) return false;
- if ( v.size() >= 3 )
- {
- if ( !isClockwise( v[_NumCommittedEdges], pt, v[_NumCommittedEdges+1] ) ) return false;
- // test first new edge
- for ( int i = 0; i+1 < (int) v.size(); i++ ) if ( i != _NumCommittedEdges && i+1 != _NumCommittedEdges )
- if ( intersects( v[i], v[i+1], v[_NumCommittedEdges], pt ) )
- return false;
- // test second new edge
- for ( int i = 1; i+1 < (int) v.size(); i++ ) if ( i != (_NumCommittedEdges+1)%(v.size()-1) && i+1 != _NumCommittedEdges+1 )
- if ( intersects( v[i], v[i+1], pt, v[_NumCommittedEdges+1] ) )
- return false;
- _DoubledArea += NewAreaFor( pt );
- }
- // TODO: assert( make sure polygon isn't goofy ) ??
- _UsedX |= 1LL << pt.x;
- _UsedY |= 1LL << pt.y;
- if ( v.empty() )
- v.push_back( pt );
- v.insert( v.begin() + _NumCommittedEdges + 1, pt );
- return true;
- }
- void RemoveLast()
- {
- XY pt = v[_NumCommittedEdges + 1];
- assert( _UsedX & (1LL << pt.x) );
- assert( _UsedY & (1LL << pt.y) );
- _UsedX &= ~(1LL << pt.x);
- _UsedY &= ~(1LL << pt.y);
- v.erase( v.begin() + _NumCommittedEdges + 1 );
- if ( v.size() >= 3 )
- _DoubledArea -= NewAreaFor( pt );
- if ( v.size() == 1 ) v.pop_back(); // first point was duplicated at the end
- }
- // say we've committed two edges, and we're adding a new point 'p'
- // The new point creates triangle <2,p,3>
- // But if we can remove <1,2,3> and add <1,p,3> and <1,2,p> would give same polygon (with one less committed edge)
- bool IsRedundant( const XY& p ) const
- {
- if ( _NumCommittedEdges <= 0 ) return false;
- const XY& v1 = v[_NumCommittedEdges-1];
- const XY& v2 = v[_NumCommittedEdges];
- const XY& v3 = v[_NumCommittedEdges+1];
- if ( !isClockwise( v2, p, v3 ) ) return false;
- if ( !isClockwise( v1, v2, p ) ) return false;
- if ( !isClockwise( v1, p, v3 ) ) return false;
- if ( !isClockwise( v1, v2, v3 ) ) return false;
- // can remove <1,2,3>? yes, if no vertices are inside
- for ( int i = 0; i < _NumCommittedEdges - 1; i++ )
- if ( isInOrOnClockwiseTriangle( v1, v2, v3, v[i] ) )
- return false;
- for ( int i = _NumCommittedEdges+2; i < (int)v.size(); i++ )
- if ( isInOrOnClockwiseTriangle( v1, v2, v3, v[i] ) )
- return false;
- return true;
- }
- bool CommittingWouldClose() const { return _NumCommittedEdges+2 >= (int) v.size(); }
- bool CanClose() const { return NumPoints() == N || !CommittingWouldClose(); }
- bool CommitEdge()
- {
- if ( !CanClose() )
- return false;
- if ( _NumCommittedEdges+1 >= (int) v.size() )
- return false;
- int slopeId = SlopeInfo<N>::IdOf( v[_NumCommittedEdges+1] - v[_NumCommittedEdges] );
- if ( _UsedSlope[slopeId] )
- return false;
- _NumCommittedEdges++;
- _UsedSlope[slopeId] = true;
- return true;
- }
- void UnCommitEdge()
- {
- _NumCommittedEdges--;
- int slopeId = SlopeInfo<N>::IdOf( v[_NumCommittedEdges+1] - v[_NumCommittedEdges] );
- assert( _UsedSlope[slopeId] );
- _UsedSlope[slopeId] = false;
- }
- int NumPoints() const { return v.size() == 0 ? 0 : (int)v.size() - 1; }
- vector<XY> pointsWithHull() const
- {
- const XY offset = HULL_SIZE == 3 ? XY( 2, 2 ) : XY( 2, 2 );
- vector<XY> ret;
- for ( int i = 1; i < (int) v.size()-1; i++ )
- ret.push_back( v[i] + offset );
- ret.push_back( v[0] + offset );
- if ( HULL_SIZE == 3 )
- {
- ret.push_back( XY(1,N+2) );
- ret.push_back( XY(N+3,N+3) );
- ret.push_back( XY(N+2,1) );
- }
- else
- {
- ret.push_back( XY(1,N+3) );
- ret.push_back( XY(N+2,N+4) );
- ret.push_back( XY(N+4,N+2) );
- ret.push_back( XY(N+3,1) );
- }
- return ret;
- }
- double Area() const { return doubleArea( DO_MAX ? pointsWithHull() : v ) / 2.; }
- string Str() const
- {
- stringstream ss;
- if ( DO_MAX && v.size()-1 == N )
- {
- ss << str( pointsWithHull(), 0 );
- }
- else
- {
- ss << str( v, 1 );
- }
- return ss.str();
- }
- uint64_t HashCode() const
- {
- }
- public:
- vector<XY> v;
- char _UsedSlope[N*N*2];
- uint64_t _UsedX;
- uint64_t _UsedY;
- int _NumCommittedEdges;
- int _DoubledArea;
- };
- class Searcher
- {
- public:
- Searcher()
- {
- _NumNodes.resize(N+2);
- if ( DO_MAX )
- {
- _Poly.AddPoint( XY( 0, 1 ) );
- _Poly.AddPoint( XY( 1, 0 ) );
- _Poly.CommitEdge();
- Go();
- }
- else
- {
- XY p0 = XY( 0, 0 );
- for ( p0.y = 0; p0.y <= N/2; p0.y++ )
- {
- if ( !_Poly.AddPoint( p0 ) ) continue;
- XY p1;
- for ( p1.x = 0; p1.x < N; p1.x++ )
- for ( p1.y = 0; p1.y < N; p1.y++ )
- {
- //if ( p0.x == p0.y && p1.x > p1.y ) continue; //clockwise/counter-clockwise matters
- if ( abs( gcd( p1.x - p0.x, p1.y - p0.y ) ) != 1 ) continue;
- if ( !_Poly.AddPoint( p1 ) ) continue;
- if ( _Poly.CommitEdge() )
- {
- //trace << _Poly.Str() << "..." << endl;
- Go();
- _Poly.UnCommitEdge();
- }
- _Poly.RemoveLast();
- }
- _Poly.RemoveLast();
- }
- }
- //trace << "nodesAtEachDepth[] = " << vstr( _NumNodes ) << endl;
- trace << "#nodes = " << accumulate(_NumNodes.begin(), _NumNodes.end(), 0LL) << endl;
- }
- void Go()
- {
- _NumNodes[_Poly.v.size()]++;
- if ( _Poly.IsClosed() )
- {
- assert( _Poly.NumPoints() == N );
- stringstream ss;
- ss << "### " << searchName() << " " << _Poly.Area() << " " << _Poly.Str() << endl;
- static set<string> s_printed;
- if ( s_printed.count( ss.str() ) )
- return;
- s_printed.insert( ss.str() );
- trace << ss.str();
- if ( _Poly._DoubledArea < g_BEST )
- g_BEST = _Poly._DoubledArea;
- return;
- }
- if ( _Poly.CommitEdge() )
- {
- Go();
- _Poly.UnCommitEdge();
- }
- if ( _Poly.NumPoints() == N ) return;
- if ( _Poly.MinimumArea() == g_BEST ) // optimization (optional)
- {
- XY a = _Poly.v[_Poly._NumCommittedEdges];
- XY b = _Poly.v[_Poly._NumCommittedEdges+1];
- LatticeLineSegment seg = findLatticePointsOnLine( b.y-a.y, a.x-b.x, 1+a.x*(b.y-a.y)-a.y*(b.x-a.x), 0, N );
- XY p = seg.pt0;
- for ( int i = 0; i < seg.n; i++, p += seg.dir )
- {
- //if ( abs((p.x-p.y)-(a.x-a.y)) > 2 && abs((p.x-p.y)-(b.x-b.y)) > 2 ) continue;
- //if ( !( abs((a.x-a.y) - (p.x-p.y)) <= 1 || abs((b.x-b.y) - (p.x-p.y)) <= 1 ) ) continue;
- assert( _Poly.NewAreaFor( p ) == 1 );
- if ( _Poly._UsedY & (1LL << p.y) ) continue;
- if ( _Poly._UsedX & (1LL << p.x) ) continue;
- if ( _Poly.IsRedundant( p ) ) continue;
- if ( !_Poly.AddPoint( p ) ) continue;
- Go();
- _Poly.RemoveLast();
- }
- }
- else
- {
- XY p;
- for ( p.y = 0; p.y < N; p.y++ ) if ( !( _Poly._UsedY & (1LL << p.y) ) )
- for ( p.x = 0; p.x < N; p.x++ ) if ( !( _Poly._UsedX & (1LL << p.x) ) )
- {
- if ( _Poly.MinimumAreaWith( p ) > g_BEST ) continue;
- if ( _Poly.IsRedundant( p ) ) continue;
- if ( !_Poly.AddPoint( p ) ) continue;
- Go();
- _Poly.RemoveLast();
- }
- }
- }
- public:
- Poly _Poly;
- vector<int64_t> _NumNodes;
- };
- }
- void main()
- {
- Timer t;
- Shards::Searcher searcher;
- trace << "Time = " << t.ElapsedTime() << " " << searchName() << " BEST=" << g_BEST << endl;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement