コンテスト中解けるべきだったのに解けなかったので自戒を込めて記事にします
問題概要
二次元平面上に相異なる個の点があるので、各点を回まで使って、三角形をできるだけ多く作ってください。何個作れますか?
ぐらいです
こう考えればよい!
まず、すべてが一直線上にある場合、三角形は作れないので、答えはです
つぎに、ほとんどが一直線上にあるけど個だけその直線上にない場合、個だけ作れます
個の場合は個作れます
個の場合は個作れます
こうしていって、 何個まで作れるんだっけ?と考えていくことにしたいが、もともとどんなケースでも 個より多く作ることはできません (上界)
実は、通る点が一番多い直線を考えて、その直線を通らない点の個数をとすると、答えは です!
これだけ!!!!!
最悪ケース (一番なにも作れないケース) から順に考えていくと、自然に解くことができました!!
ちなみに、「通る点が一番多い直線」だけを考える、で本当に良いの?番目や番目を意識しなくていいの?
と思うかもしれません (自分も、それが頭をよぎって、こんな単純な方針が見えなかった)
(点がランダムに散らばってる場合など) たいていの場合答えは になるので、答えが にならなくなるギリギリを考えてみます
のとき、 です
つまり、通常個作ることができます
このとき、個までは一直線上にあって大丈夫です
個めが一直線上にある場合にはじめて、答えが減少します
答えに影響するのは、 より多い点が一直線上にあるときに限るとわかります
より多い点が一直線上にあるとき、別の直線上に より多い点があることはどう頑張ってもありませんよね。
これが番目番目...に通る点が多い直線を考えなくて良い理由です!
教訓
最悪ケースを考えるべし!!!!
ACコード (コンテスト後)
lines[(i,j)]
に、点iと点jを通る直線に何個の点が含まれますか?を管理しています
is_line()
は3点が同一直線上にあるかの判定 (傾きの比較を変形して割り算を回避してます) このスニペット持ってると役立ちそう
N = int(input()) XY = [tuple(map(int,input().split())) for _ in range(N)] def is_line(x1,y1,x2,y2,x3,y3): return (x2-x1)*(y3-y1) == (y2-y1)*(x3-x1) lines = {(0,1): 2} for k in range(2,N): xk,yk = XY[k] add = [] for (i,j) in lines.keys(): xi,yi = XY[i] xj,yj = XY[j] if is_line(xi,yi,xj,yj,xk,yk): lines[(i,j)] += 1 else: add.append((i,k)) add.append((j,k)) for i,j in add: lines[(i,j)] = 2 line_max = max(lines.values()) other = N - line_max ans = min(N//3, other) print(ans)