Problem:


Convex Hull

Find the convex hull of a given set of points P. In other words, find the smallest convex polygon containing all the points of P. Here, in a convex polygon, all interior angles are less than or equal to 180 degrees.

Please note that you should find all the points of P on both corner and boundary of the convex polygon.

Input

n
x1 y1 
x2 y2
:
xn yn

The first integer n is the number of points in P. The coordinate of the i-th point pi is given by two integers xi and yi. 

Output

In the first line, print the number of points on the corner/boundary of the convex polygon. In the following lines, print x y coordinates of the set of points.
The coordinates should be given in the order of counter-clockwise visit of them starting from the point in P with the minimum y-coordinate, or the leftmost such point in case of a tie.

Constraints

3 ≤ n ≤ 100000

-10000 ≤ xi, yi ≤ 10000

No point in the P will occur more than once.

Sample Input 1

7
2 1
0 0
1 2
2 2
4 2
1 3
3 3

Sample Output 1

5
0 0
2 1
4 2
3 3
1 3

Sample Input 2

4
0 0
2 2
0 2
0 1

Sample Output 2

4
0 0
2 2
0 2
0 1



Sample input:
7
2 1
0 0
1 2
2 2
4 2
1 3
3 3


Sample output:
5
0 0
2 1
4 2
3 3
1 3

Submission status: Accepted

Solution code:
from collections import deque
from functools import singledispatch
import math

EPS = 1e-10

COUNTER_CLOCKWISE = 1
CLOCKWISE = -1
ONLINE_BACK = 2
ONLINE_FRONT = -2
ON_SEGMENT = 0

class Segment():
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

class Circle():
    def __init__(self, c, r):
        self.c = c
        self.r = r

class Point():
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, point):
        # + 演算子での挙動を指定
        return Point(self.x+point.x, self.y+point.y)

    def __sub__(self, point):
        # - 演算子での挙動を指定
        return Point(self.x-point.x, self.y-point.y)

    def __mul__(self, a):
        # * 演算子での挙動を指定
        return Point(a*self.x, a*self.y)

    def __truediv__(self, a):
        # / 演算子での挙動を指定
        return Point(self.x/a, self.y/a)

    def __abs__(self):
        # abs関数での挙動を指定
        return math.sqrt(self.norm())

    def norm(self):
        return self.x**2+self.y**2

    def __lt__(self, p):
        # < 演算子での挙動を指定、これによってsortで並び替えが可能になる。
        # boolean = 
        return (self.x < p.x) if (self.x != p.x) else (self.y < p.y)

    def __eq__(self, p):
        # == 演算子での挙動を指定
        return abs(self.x-p.x) < EPS and abs(self.y-p.y) <EPS

def dot(a, b):
    return a.x*b.x+a.y*b.y

def cross(a, b):
    return a.x*b.y - a.y*b.x

def isOrthogonal(a, b):
    return dot(a, b) == 0

def isParallel(a, b):
    return cross(a, b) == 0

def project(s, p):
    #s: Segment(), p: Point()
    base = s.p2 - s.p1
    r = dot(p-s.p1, base)/base.norm()
    return s.p1+base*r

def reflect(s, p):
    return p+(project(s, p)-p)*2

@singledispatch
def get_distance(a, b):
    return abs(a-b)

def get_distance_lp(l, p):
    return abs(cross(l.p2-l.p1, p-l.p1)/abs(l.p2-l.p1))

def get_distance_sp(s, p):
    if dot(s.p2-s.p1, p-s.p1) < 0:
        return abs(p-s.p1)
    if dot(s.p1-s.p2, p-s.p2) < 0:
        return abs(p-s.p2)
    return get_distance_lp(s, p)
    
@get_distance.register(Segment)
def _(s1, s2):
    if intersect(s1, s2):
        return 0
    return min([get_distance_sp(s1, s2.p1), get_distance_sp(s1, s2.p2),
                get_distance_sp(s2, s1.p1), get_distance_sp(s2, s1.p2)])

def ccw(p0, p1, p2):
    a = p1 - p0
    b = p2 - p0
    if cross(a, b) > EPS:
        return COUNTER_CLOCKWISE
    if cross(a, b) < -EPS:
        return CLOCKWISE
    if dot(a, b) < -EPS:
        return ONLINE_BACK
    if a.norm() < b.norm():
        return ONLINE_FRONT
    return ON_SEGMENT

@singledispatch
def intersect(p1, p2, p3, p4):
    return (ccw(p1, p2, p3)*ccw(p1, p2, p4) <= 0
            and ccw(p3, p4, p1)*ccw(p3, p4, p2) <= 0)

@intersect.register(Segment)
def _(s1, s2):
    return intersect(s1.p1, s1.p2, s2.p1, s2.p2)

def get_cross_point(s1, s2):
    base = s2.p2 - s2.p1
    d1 = abs(cross(base, s1.p1-s2.p1))
    d2 = abs(cross(base, s1.p2-s2.p1))
    t = d1/(d1+d2)
    return s1.p1 + (s1.p2-s1.p1)*t

def get_cross_points(c, l):
    # 次の行のコメントのコードは、新しいintersect関数を作る必要があり(おそらくpython3.7以上必須)、手間取るので割愛します。
    # assert(intersect(c, l))
    pr = project(l, c.c)
    e = (l.p2 - l.p1)/abs(l.p2-l.p1)
    base = (c.r**2-(pr-c.c).norm())**0.5
    return (pr+e*base, pr-e*base)

def arg(p):
    return math.atan2(p.y, p.x)

def polar(a, r):
    return Point(math.cos(r)*a, math.sin(r)*a)

def get_cross_points_(c1, c2):
    # 関数名かぶるので、_つけました。
    # python3.7以上でsingledispatchを使えば同じ関数名も可能ですが、
    # atcoderのpython3.4も想定しているため、関数名を変えておきます。
    # assert(intersect(c, c))
    d = abs(c1.c-c2.c)
    a = math.acos((c1.r**2+d**2-c2.r**2)/(2*c1.r*d))
    t = arg(c2.c-c1.c)
    return (c1.c+polar(c1.r, t+a), c1.c+polar(c1.r, t-a))

def contains(g, p):
    # gは多角形の頂点を順番に並べたリスト/タプル
    n = len(g)
    x = False
    for i in range(n):
        a = g[i]-p
        b = g[(i+1)%n]-p
        if abs(cross(a, b)) < EPS and dot(a, b) < EPS:
            return 1
        if a.y > b.y:
            a, b = b, a
        if a.y < EPS and EPS < b.y and cross(a, b) > EPS:
            x = not x
    return 2 if x else 0

def andrewScan(s):
    u = deque()     # Polygonクラスはキューで代用可能
    l = deque()
    if len(s) < 3:
        if ccw(s[0], s[1], s[2]) != CLOCKWISE:
            s.reverse()
        return s
    s.sort()    # Pointクラスで<演算子が使えるようにしてあるので、sortができる
    u.append(s[0])
    u.append(s[1])
    l.append(s[-1])
    l.append(s[-2])

    for i in range(2, len(s)):
        for n in range(len(u), 1, -1):
            if ccw(u[n-2], u[n-1], s[i]) != CLOCKWISE:
                break
            u.pop()
        u.append(s[i])
    
    for i in range(len(s)-3, -1, -1):
        for n in range(len(l), 1, -1):
            if ccw(l[n-2], l[n-1], s[i]) != CLOCKWISE:
                break
            l.pop()
        l.append(s[i])
    
    l.reverse()
    u = list(u)
    for i in range(len(u)-2, 0, -1):
        l.append(u[i])
    return l



if __name__ == '__main__':
    from sys import stdin
    input = stdin.readline

    n = int(input())
    points = [0]*n
    for i in range(n):
        points[i] = Point(*map(int, input().split()))

    l = andrewScan(points)
    l.reverse()    # 反時計回りにする
    l = list(l)    # deqeuのままだと処理が面倒なのでリストに
    min_index = l.index(min(l, key=lambda p: (p.y, p.x)))
    print(len(l))
    for i in l[min_index:]:
        print(i.x, i.y)
    for i in l[:min_index]:
        print(i.x, i.y)

Submission status: Accepted

Solution code:
#!usr/bin/env python3
from collections import defaultdict, deque
from heapq import heappush, heappop
from itertools import permutations, accumulate
import sys
import math
import bisect
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def I(): return int(sys.stdin.readline())
def LS():return [list(x) for x in sys.stdin.readline().split()]
def S():
    res = list(sys.stdin.readline())
    if res[-1] == "\n":
        return res[:-1]
    return res
def IR(n):
    return [I() for i in range(n)]
def LIR(n):
    return [LI() for i in range(n)]
def SR(n):
    return [S() for i in range(n)]
def LSR(n):
    return [LS() for i in range(n)]

sys.setrecursionlimit(1000000)
mod = 1000000007

def solve():
    def det(a,b):
        return a[0]*b[1]-a[1]*b[0]

    def minus(a,b):
        return (a[0]-b[0],a[1]-b[1])

    def convex_hull(ps):
        n = len(ps)
        ps.sort(key = lambda x:(x[1],x[0]))
        k = 0
        qs = [0]*(n+2)
        for i in range(n):
            while k > 1 and det(minus(qs[k-1], qs[k-2]), minus(ps[i], qs[k-1])) < 0:
                k -= 1
            qs[k] = ps[i]
            k += 1
        t = k
        for i in range(n-1)[::-1]:
            while k > t and det(minus(qs[k-1], qs[k-2]), minus(ps[i], qs[k-1])) < 0:
                k -= 1
            qs[k] = ps[i]
            k += 1
        qs = qs[:min(n,k-1)]
        return qs
    n = I()
    ps = LIR(n)
    qs = convex_hull(ps)
    print(len(qs))
    for i in qs:
        print(*i)
    return

#Solve
if __name__ == "__main__":
    solve()

Submission status: Accepted

Solution code:
n, *L = map(int, open(0).read().split())
P = [complex(x, y) for x, y in zip(*[iter(L)] * 2)]

def CH(P: list):
	def cross(z1,z2):
		return z1.real*z2.imag-z1.imag*z2.real
	P.sort(key=lambda z:(z.imag,z.real))
	hull=[]
	cur,pre=0,1
	for Q in (P,P[len(P)-2::-1]):
		for z in Q:
			while cur>pre and cross(hull[-2]-hull[-1],z-hull[-1])>0:
				hull.pop()
				cur-=1
			hull.append(z)
			cur+=1
		pre=len(hull)
	return hull[:-1]

ret=CH(P)
print(len(ret))
for z in ret:
	print(int(z.real),int(z.imag))

Submission status: Accepted

Solution code:
from math import pi, cos, sin, atan2
from operator import itemgetter
EPS = 10**(-9)

def eq(value1, value2):
    return abs(value1-value2) <= EPS

class Point():
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __str__(self):
        #return "{0:.8f} {1:.8f}".format(self.x, self.y)
        return "{0} {1}".format(self.x, self.y)
    
    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)
    
    def __sub__(self, other):
        return Point(self.x - other.x, self.y - other.y)
    
    def __mul__(self, scal):
        return Point(self.x*scal, self.y*scal)
    
    def __truediv__(self, scal):
        return Point(self.x/scal, self.y/scal)
    
    def __eq__(self, other):
        return eq(self.x, other.x) and eq(self.y, other.y)

    # 原点からの距離
    def __abs__(self):
        return (self.x**2+self.y**2)**0.5

    def arg(self):
        return atan2(self.y, self.x)
    
# 原点を中心にrad角だけ回転した点
def Rotation(vec: Point, rad):
    return Point(vec.x*cos(rad)-vec.y*sin(rad), vec.x*sin(rad)+vec.y*cos(rad))


class Circle():
    def __init__(self, p, r):
        self.p = p
        self.r = r


class Line():
    # 点a, bを通る
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.arg = (a-b).arg % pi
    
    def __str__(self):
        return "[({0}, {1}) - ({2}, {3})]".format(self.a.x, self.a.y, self.b.x, self.b.y)

    # pointを通って平行
    def par(self, point):
        return Line(point, point+(self.a-self.b))

    # pointを通って垂直
    def tan(self, point):
        return Line(point, point + Rotation(self.a-self.b, pi/2))


class Segment(Line):
    def __init__(self, a, b):
        super().__init__(a, b)


# 符号付き面積
def cross(vec1: Point, vec2: Point):
    return vec1.x*vec2.y - vec1.y*vec2.x

# 内積
def dot(vec1: Point, vec2: Point):
    return vec1.x*vec2.x + vec1.y*vec2.y

# 点a->b->cの回転方向
def ccw(a, b, c):
    if cross(b-a, c-a) > EPS: return +1 # COUNTER_CLOCKWISE
    if cross(b-a, c-a) < -EPS: return -1 # CLOCKWISE
    if dot(c-a, b-a) < -EPS: return +2 # c -> a -> b
    if abs(b-a) < abs(c-a): return -2 # a -> b -> c
    return 0 # a -> c -> b


# pのlへの射影
def projection(l, p):
    t = dot(l.b-l.a, p-l.a) / abs(l.a-l.b)**2
    return l.a + (l.b-l.a)*t

# pのlによる反射
def reflection(l, p):
    return p + (projection(l, p) - p)*2

def isPararell(l1, l2):
    return eq(cross(l1.a-l1.b, l2.a-l2.b), 0)

def isVertical(l1, l2):
    return eq(dot(l1.a-l1.b, l2.a-l2.b), 0)


def Intersect_lp(l, p):
    return abs(ccw(l.a, l.b, p)) != 1

def Intersect_ll(l1, l2):
    return not isPararell(l1, l2) or Intersect_lp(l1, l2.a)

def Intersect_sp(s, p):
    return ccw(s.a, s.b, p) == 0

def Intersect_ss(s1, s2):
    return ccw(s1.a, s1.b, s2.a)*ccw(s1.a, s1.b, s2.b) <= 0 and ccw(s2.a, s2.b, s1.a)*ccw(s2.a, s2.b, s1.b) <= 0

def Intersect_ls(l, s):
    return cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) < EPS

def Intersect_cp(c, p):
    return abs(abs(c.p - p) - c.r) < EPS

def Intersect_cl(c, l):
    return distance_lp(l, c.p) <= c.r + EPS

def Intersect_cs(c, s):
    h = projection(s, c.p)
    if abs(h - c.p)**2 - c.r**2 > EPS: return 0 # 遠い
    d1 = abs(c.p - s.a); d2 = abs(c.p - s.b)
    if d1 < c.r - EPS and d2 < c.r - EPS: return 0 # どちらも内側
    if (d1 < c.r - EPS and d2 > c.r + EPS) or (d1 > c.r + EPS and d2 < c.r - EPS): return 1 # 片方だけ内側
    if dot(s.a-h, s.b-h) < 0: return 2 # 2点で交わる
    return 0


def Intersect_cc(c1, c2):
    if c1.r < c2.r:
        c1, c2 = c2, c1
    d = abs(c1.p - c2.p)
    if eq(c1.r + c2.r, d): return 3 # 内接
    if eq(c1.r - c2.r, d): return 1 # 外接
    if c1.r + c2.r < d: return 4 # 含まれてる
    if c1.r - c2.r < d: return 2 # 2交点持つ
    return 0 # 離れてる


def distance_pp(p1, p2):
    return abs(p1-p2)

def distance_lp(l, p):
    return abs(projection(l,p)-p)

def distance_ll(l1, l2):
    return 0 if Intersect_ll(l1, l2) else distance_lp(l1, l2.a)

def distance_sp(s, p):
    r = projection(s, p)
    if Intersect_sp(s, r): return abs(r-p)
    return min(abs(s.a-p), abs(s.b-p))

def distance_ss(s1, s2):
    if Intersect_ss(s1, s2): return 0
    return min([distance_sp(s1, s2.a), distance_sp(s1, s2.b), distance_sp(s2, s1.a), distance_sp(s2, s1.b)])

def distance_ls(l, s):
    if Intersect_ls(l, s): return 0
    return min(distance_lp(l, s.a), distance_lp(l, s.b))


def crosspoint_ll(l1, l2):
    A = cross(l1.b - l1.a, l2.b - l2.a)
    B = cross(l1.b - l1.a, l1.b - l2.a)
    if eq(abs(A), 0) and eq(abs(B), 0): return l2.a
    return l2.a + (l2.b - l2.a) * B / A

def crosspoint_ss(s1, s2):
    return crosspoint_ll(s1, s2)

def crosspoint_lc(l, c):
    p = projection(l, c.p)
    if eq(distance_lp(l, c.p), c.r): return [p]
    e = (l.b - l.a) / abs(l.b-l.a)
    dis = (c.r**2-abs(p-c.p)**2)**0.5
    return [p + e*dis, p - e*dis]

def crosspoint_sc(s, c):
    l = Line(s.a, s.b)
    if Intersect_cs(c, s) == 2: return crosspoint_lc(l, c)
    Points = crosspoint_lc(l, c)
    if len(Points) == 1:
        return Points
    if dot(s.a-Points[0], s.b-Points[0]) < 0: return [Points[0]]
    return [Points[1]]


def crosspoint_cc(c1, c2):
    d = abs(c1.p-c2.p)
    if not abs(c1.r-c2.r) <= d <= c1.r+c2.r:
        return []
    mid_p = (c2.p * (c1.r**2-c2.r**2+d**2) + c1.p * (c2.r**2-c1.r**2+d**2)) / (2*d**2)
    tanvec = Rotation(c1.p-c2.p, pi/2)
    return crosspoint_lc(Line(mid_p, mid_p+tanvec), c1)


# pからのcの接点
def tangent_cp(c, p):
    return crosspoint_cc(c, Circle(p, (abs(p-c.p)**2 - c.r**2)**0.5))

# 共通接線
def tangent_cc(c1, c2):
    pass


def isConvex(Points):
    n = len(Points)
    for i in range(n):
        if ccw(Points[i-1], Points[i], Points[(i+1)%n]) == -1: return False
    return True


def ConvexHull(Points):
    Points.sort(key=lambda p: p.y)
    Points.sort(key=lambda p: p.x)

    Qs = []
    for p in Points:
        while len(Qs) > 1 and ccw(Qs[-2], Qs[-1], p) == -1:
            Qs.pop()
        Qs.append(p)
        
    t = len(Qs)
    Qs.pop()
    for p in reversed(Points):
        while len(Qs) > t and ccw(Qs[-2], Qs[-1], p) == -1:
            Qs.pop()
        Qs.append(p)
    Qs.pop()

    return Qs


import sys
input = sys.stdin.readline

def verify_1A():
    p1x, p1y, p2x, p2y = map(int, input().split())
    l = Line(Point(p1x, p1y), Point(p2x, p2y))
    Q = int(input())
    Query = [list(map(int, input().split())) for _ in range(Q)]
    for px, py in Query:
        p = Point(px, py)
        print(projection(l, p))

def verify_1B():
    p1x, p1y, p2x, p2y = map(int, input().split())
    l = Line(Point(p1x, p1y), Point(p2x, p2y))
    Q = int(input())
    Query = [list(map(int, input().split())) for _ in range(Q)]
    for px, py in Query:
        p = Point(px, py)
        print(reflection(l, p))

def verify_1C():
    p1x, p1y, p2x, p2y = map(int, input().split())
    p1 = Point(p1x, p1y); p2 = Point(p2x, p2y)
    Q = int(input())
    Query = [list(map(int, input().split())) for _ in range(Q)]
    for px, py in Query:
        p = Point(px, py)
        result = ccw(p1, p2, p)
        if result == 1:
            print("COUNTER_CLOCKWISE")
        elif result == -1:
            print("CLOCKWISE")
        elif result == 2:
            print("ONLINE_BACK")
        elif result == -2:
            print("ONLINE_FRONT")
        else:
            print("ON_SEGMENT")

def verify_2A():
    Q = int(input())
    Query = [list(map(int, input().split())) for _ in range(Q)]
    for p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y in Query:
        l1 = Line(Point(p0x, p0y), Point(p1x, p1y))
        l2 = Line(Point(p2x, p2y), Point(p3x, p3y))
        if isPararell(l1, l2):
            print(2)
        elif isVertical(l1, l2):
            print(1)
        else:
            print(0)

def verify_2B():
    Q = int(input())
    Query = [list(map(int, input().split())) for _ in range(Q)]
    for p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y in Query:
        s1 = Segment(Point(p0x, p0y), Point(p1x, p1y))
        s2 = Segment(Point(p2x, p2y), Point(p3x, p3y))
        if Intersect_ss(s1, s2):
            print(1)
        else:
            print(0)


def verify_2C():
    Q = int(input())
    Query = [list(map(int, input().split())) for _ in range(Q)]
    for p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y in Query:
        s1 = Segment(Point(p0x, p0y), Point(p1x, p1y))
        s2 = Segment(Point(p2x, p2y), Point(p3x, p3y))
        print(crosspoint_ss(s1, s2))  

def verify_2D():
    Q = int(input())
    Query = [list(map(int, input().split())) for _ in range(Q)]
    for p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y in Query:
        s1 = Segment(Point(p0x, p0y), Point(p1x, p1y))
        s2 = Segment(Point(p2x, p2y), Point(p3x, p3y))
        print("{:.8f}".format(distance_ss(s1, s2)))

def verify_4A():
    Q = int(input())
    Points = [Point(*map(int, input().split())) for _ in range(Q)]
    Qs = ConvexHull(Points)
    min_y = 10**10
    min_x = 10**10
    for i, p in enumerate(Qs):
        if p.y < min_y:
            min_y = p.y
            min_x = p.x
            min_ind = i
        elif p.y == min_y and p.x < min_x:
            min_x = p.x
            min_ind = i
    Qs = Qs[min_ind:] + Qs[:min_ind]
    print(len(Qs))
    print("\n".join([str(p.x) + " " + str(p.y) for p in Qs]))

verify_4A()

def verify_7A():
    c1x, c1y, c1r = map(int, input().split())
    c2x, c2y, c2r = map(int, input().split())
    print(Intersect_cc(Circle(Point(c1x, c1y), c1r), Circle(Point(c2x, c2y), c2r)))

def verify_7D():
    cx, cy, cr = map(int, input().split())
    c = Circle(Point(cx, cy), cr)
    Q = int(input())
    Query = [list(map(int, input().split())) for _ in range(Q)]
    for x1, y1, x2, y2 in Query:
        Points = crosspoint_lc(Line(Point(x1, y1), Point(x2,y2)), c)
        if len(Points) == 1:
            Points.append(Points[-1])
        Points.sort(key=lambda p: p.y)
        Points.sort(key=lambda p: p.x)
        print(*Points)

def verify_7E():
    c1x, c1y, c1r = map(int, input().split())
    c1 = Circle(Point(c1x, c1y), c1r)
    c2x, c2y, c2r = map(int, input().split())
    c2 = Circle(Point(c2x, c2y), c2r)

    Points = crosspoint_cc(c1, c2)
    if len(Points) == 1:
        Points.append(Points[-1])
    Points.sort(key=lambda p: p.y)
    Points.sort(key=lambda p: p.x)
    print(*Points)  

def verify_7F():
    px, py = map(int, input().split())
    cx, cy, cr = map(int, input().split())
    Points = tangent_cp(Circle(Point(cx, cy), cr), Point(px, py))
    if len(Points) == 1:
        Points.append(Points[-1])
    Points.sort(key=lambda p: p.y)
    Points.sort(key=lambda p: p.x)
    print(*Points, sep="\n")

Submission status: Accepted

Solution code:
# used in AOJ No.68, yukicoder No.199
# complexity: O(n^(1/2))
eps = 1e-10


def add(a, b):
    return 0 if abs(a + b) < eps * (abs(a) + abs(b)) else a + b


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, p):
        return Point(add(self.x, p.x), add(self.y, p.y))

    def __sub__(self, p):
        return Point(add(self.x, -p.x), add(self.y, -p.y))

    def __mul__(self, d):
        return Point(self.x * d, self.y * d)

    def dot(self, p):
        return add(self.x * p.x, self.y * p.y)

    def det(self, p):
        return add(self.x * p.y, -self.y * p.x)

    def __str__(self):
        return "({}, {})".format(self.x, self.y)


def convex_hull(ps):
    ps = [Point(x, y) for x, y in sorted([(p.x, p.y) for p in ps])]
    lower_hull = get_bounds(ps)
    ps = ps[::-1]
    upper_hull = get_bounds(ps)
    del upper_hull[-1]
    del lower_hull[-1]
    lower_hull.extend(upper_hull)
    return lower_hull


def get_bounds(ps):
    qs = [ps[0], ps[1]]
    for p in ps[2:]:
        while len(qs) > 1 and (qs[-1] - qs[-2]).det(p - qs[-1]) < 0:
            del qs[-1]
        qs.append(p)
    return qs


def aoj():
    N = int(input())
    ps = []
    for _ in range(N):
        x, y = map(int, input().split())
        ps.append(Point(x, y))
    convex = convex_hull(ps)
    print(len(convex))
    min_idx = -1
    min_x, min_y = 10001, 10001
    for i, p in enumerate(convex):
        if p.y < min_y or (p.y == min_y and p.x < min_x):
            min_idx = i
            min_x, min_y = p.x, p.y
    for p in convex[min_idx:]:
        print(p.x, p.y)
    for p in convex[:min_idx]:
        print(p.x, p.y)


if __name__ == '__main__':
    aoj()

Submission status: Accepted

Solution code:
# coding: utf-8
# Your code here!

# coding: utf-8
# Your code here!
import math

EPS = 0.0000000001

COUNTER_CLOCKWISE = 1
CLOCKWISE = -1
ONLINE_BACK = 2
ONLINE_FRONT = -2
ON_SEGMENT = 0

IN = 2
ON = 1
OUT = 0

class Point:
    
    global EPS
    
    def __init__(self, x = 0, y = 0):
        self.x = x
        self.y = y
        
        
    def __add__(a, b):
        s = a.x + b.x
        t = a.y + b.y
        return Point(s, t)
        
    def __sub__(a, b):
        s = a.x - b.x
        t = a.y - b.y
        return Point(s, t)
            
    def __mul__(self, a):
        s = a * self.x
        t = a * self.y
        return Point(s, t)
        
    def __truediv__(self, a):
        s = self.x / a
        t = self.y / a
        return Point(s, t)
            
            
            
            
    def norm(self):
        return self.x * self.x + self.y * self.y
        
    def abs(self):
        return self.norm() ** 0.5
            
    
            
            
    def __eq__(self, other):
        return abs(self.x - other.y) < self.EPS and abs(self.y - other.y) < self.EPS
            
            
            
    def dot(self, b):
        return self.x * b.x + self.y * b.y
        
    def cross(self, b):
        return self.x * b.y - self.y * b.x
    
    
class Segment:
    def __init__(self, p1, p2):
        self.p1 = p1
        self.p2 = p2

class Circle:
    def __init__(self, c, r):
        self.c = c
        self.r = r

def project(s, p):
    base = s.p2 - s.p1
    hypo = p - s.p1
    r = hypo.dot(base) / base.norm()
    return s.p1 + base * r

def reflecton(s, p):
    return p + (project(s,p) - p) * 2


def getDistance(a, b):
    return (a-b).abs()

def getDistanceLP(l, p):
    return abs((l.p2-l.p1).cross(p-l.p1)) / ((l.p2-l.p1).abs())

def getDistanceSP(s, p):
    if (s.p2 - s.p1).dot(p-s.p1) < 0:
        return (p-s.p1).abs()
    elif (s.p1 - s.p2).dot(p-s.p2) < 0:
        return (p-s.p2).abs()
    return getDistanceLP(s,p)


def getDistanceSS(s1, s2):
    if intersectS(s1, s2):
        return 0
    return min(getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2), getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2))



def ccw(p0, p1, p2):
    a = p1-p0
    b = p2-p0
    
    if a.cross(b) > 0:
        return COUNTER_CLOCKWISE
    elif a.cross(b) <0:
        return CLOCKWISE
    elif a.dot(b) < 0:
        return ONLINE_BACK
    elif a.abs() < b.abs():
        return ONLINE_FRONT
    else:
        return ON_SEGMENT

def intersect(p1, p2, p3, p4):
    return ccw(p1, p2, p3) *ccw(p1, p2, p4) <=0 and ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0

def intersectS(s1, s2):
    return intersect(s1.p1, s1.p2, s2.p1, s2.p2)


def getCrossPoint(s1, s2):
    base = s2.p2-s2.p1
    a = s1.p1-s2.p1
    b = s1.p2-s2.p1
    
    d1 = abs(a.cross(base))
    d2 = abs(b.cross(base))
    
    t = d1 / (d1+d2)
    return s1.p1 + (s1.p2-s1.p1) * t


def getCrossPointC(c, l):
    pr = project(l, c.c)
    e = (l.p2-l.p1) / (l.p2-l.p1).abs()
    base = (c.r *c.r  - (pr - c.c).norm()) ** 0.5
    return pr - e * base, pr + e * base

def printPoint(p1, p2):
    print(round(p1.x, 8), round(p1.y, 8), round(p2.x, 8), round(p2.y, 8))


def arg(p):
    return math.atan2(p.y ,p.x)

def polar(a, r):
    return Point(a * math.cos(r), a * math.sin(r))

def getCrossPointCC(c1, c2):
    d = (c2.c - c1.c).abs()
    a = math.acos((c1.r * c1.r + d*d - c2.r*c2.r) / (2*c1.r*d))
    b = arg(c2.c-c1.c)
    return c1.c + polar(c1.r, b+a), c1.c + polar(c1.r, b-a)


def contains(g, p):
    n = len(g)
    flg = False
    for i in range(n):
        a = g[i] - p
        b = g[(i+1)%n] - p
        if abs(a.cross(b)) < EPS and a.dot(b) < EPS:
            return ON
        if a.y > b.y:
            a,b = b,a
        if a.y < EPS and EPS < b.y and a.cross(b) > EPS:
            flg = not flg
        
    if flg:
        return IN
    else:
        return OUT



def andrewScan(s):
    if len(s) < 3:
        return s

    s.sort(key=lambda u: u.x)
    s.sort(key=lambda u: u.y)
    
    u.append(s[0])
    u.append(s[1])
    l.append(s[-1])
    l.append(s[-2])

    for i in range(2,n):
        for j in range(len(u),-1,-1):
            if j >=2 and ccw(s[i], u[j-2], u[j-1]) == COUNTER_CLOCKWISE:
                u.pop()
            else :
                break
        u.append(s[i])
        
    
    for i in range(n-3,-1, -1):
        for j in range(len(l),-1,-1):
            if j >=2 and ccw(s[i], l[j-2], l[j-1]) == COUNTER_CLOCKWISE:
                l.pop()
            else :
                break
        l.append(s[i])





    l.reverse()
    
    for i in range(len(u)-2, 0, -1):
        l.append(u[i])
    


n = int(input())
s = []
for i in range(n):
    nums=list(map(int,input().split()))
    s.append(Point(nums[0], nums[1]))
    
u = []
l = []
andrewScan(s)

print(len(l))
for i in range(len(l)):
    print(l[i].x, l[i].y)