Problem:
ハッピーエンド問題

「ハッピーエンド問題」と呼ばれる数学の未解決問題に関連したプログラムを書いてみましょう。平面上に与えられたN個の点から、ちょうどk個の点を結んでできる凸多角形のうち、最も面積の小さいものを見つけるプログラムを作成してください。ただし、N個の点の座標を与えられた後、質問として凸多角形の角の個数kがいくつか与えられます。

（補足：ハッピーエンド問題について）

平面上にどの３点も同じ直線上に乗らないようにN個の点を置きます。そのとき、どのように点を置いても、k個の点をうまく選ぶとk個の角をもつ凸多角形が必ず作れると予想されています。 今のところ、三角形ならばN=3、四角形ならばN=5、五角形ならばN=9、六角形ならばN=17であればよいことが、２００６年までに証明されています。また、三角形以上のすべてのk角形に対して、N=1+2k-2という予想がありますが、いまだ証明されていません。これは１００年にわたり研究が進められている難問です。

この問題には、「ハッピーエンド問題」という素敵な名前がつけられています。ある男女がこの問題を研究しているうちに仲良くなって、ついに結婚したことにちなんで、友人の数学者が名付けたそうです。ロマンチックですね。

入力

入力は1つのデータセットからなる。入力データは以下の形式で与えられる。

N
x1 y1
:
xN yN
Q
k1
:
kQ

１行目に平面上の点の個数N(3 ≤ N ≤ 40)が与えられる。続くN行に各点の座標が与えられる。各点には1からNまでの番号が、入力される順番に付けられている。xiとyi(-10000 ≤ xi, yi ≤ 10000)はi番目の点のそれぞれx座標とy座標を表す整数である。x軸の正方向は右向き, y軸の正方向は上向きに取るものとする。

続く１行に質問の数Q(1 ≤ Q ≤ N)が与えられる。続くQ行に質問が与えられる。ki(3 ≤ ki ≤ N)はi番目の質問である凸多角形の角の個数を表す。

なお、入力は以下の条件を満たすものとする。

入力される点の座標はすべて異なる。

どの３点も同じ直線上には乗らない。

各質問に対して面積最小の凸多角形は１つであり、２番目に小さい凸多角形との面積の差は 0.0001以上。

出力

質問ごとに、面積が最小の凸多角形の全頂点を1行に出力する。凸多角形の頂点で最も下にあるものの中で最も左にある頂点から順に、反時計周りで頂点の番号を出力する。頂点の番号の間は空白1つで区切る。行の終わりには空白文字を出力しない。ただし、凸多角形が作れない場合はNAと出力する。

入力例 1

5
0 0
3 0
5 2
1 4
0 3
3
3
4
5

出力例 1

1 4 5
1 2 4 5
1 2 3 4 5

入力例 2

6
0 0
3 0
5 2
1 4
0 3
3 2
4
3
4
5
6

出力例 2

6 3 5
6 3 4 5
1 2 6 4 5
NA

Solution in C++11 (judge verdict: Wrong Answer)

#include<iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;

const double INF = 1e9;
const double EPS = 1e-9;

// 多角形の点列と最小面積
struct Polygon {
    vector<int> point;
    double area;
    Polygon() :area(INF) {}
};

struct Point {
    double x, y;
    Point() {}
    Point(const double x, const double y) :x(x), y(y) {}
};

const Point operator-(const Point& lhs, const Point& rhs) {return Point(lhs.x - rhs.x, lhs.y - rhs.y);}
double cross(const Point lhs, const Point& rhs) {return lhs.x * rhs.y - lhs.y * rhs.x;}
bool ccw(const Point& a, const Point& b, const Point& c) {return cross(b - a, c - a) > 0;}
bool ccw(const vector<int>& index, const vector<Point>& point) {
    for(int i = 0; i < index.size(); ++i) if(!ccw(point[index[i]], point[index[(i + 1) % index.size()]], point[index[(i + 2) % index.size()]])) return false;
    return true;
}
double area(const Point& a, const Point& b, const Point& c) {return fabs(cross(b - a, c - a)) / 2.0;}
bool operator<(const Point& lhs, const Point& rhs) {return lhs.y != rhs.y ? lhs.y < rhs.y : lhs.x < rhs.x;}
bool less_polygon(const Polygon& lhs, const Polygon& rhs, const vector<Point>& point) {
    if(lhs.area != rhs.area) return lhs.area < rhs.area + EPS;
    if(lhs.point.empty() || rhs.point.empty()) return lhs.point.size() < rhs.point.size();
    return point[lhs.point.front()] < point[rhs.point.front()];
}

int main() {
    int N;
    cin >> N;
    vector<Point> point(N);
    for(auto& p: point) cin >> p.x >> p.y;
    int Q;
    cin >> Q;
    vector<int> query(Q);
    for(auto& q: query) cin >> q;

    int apex = *max_element(query.begin(), query.end());
    vector<vector<vector<vector<Polygon>>>> dp(point.size(), vector<vector<vector<Polygon>>>(apex + 1, vector<vector<Polygon>>(point.size(), vector<Polygon>(point.size()))));
    vector<Polygon> answer(apex + 1);
    // dp 初期化
    for(int A = 0; A < point.size(); ++A) for(int B = 0; B < point.size(); ++B) for(int C = 0; C < point.size(); ++C) {
        if(!ccw(point[A], point[B], point[C])) continue;
        dp[A][3][B][C].point.push_back(A);
        dp[A][3][B][C].point.push_back(B);
        dp[A][3][B][C].point.push_back(C);
        dp[A][3][B][C].area = area(point[A], point[B], point[C]);
        if(less_polygon(dp[A][3][B][C], answer[3], point)) answer[3] = dp[A][3][B][C];
    }
    // DP
    for(int A = 0; A < point.size(); ++A) {
        for(int k = 4; k <= apex; ++k) {
            for(int B = 0; B < point.size(); ++B) {
                for(int C = 0; C < point.size(); ++C) {
                    if(!ccw(point[A], point[B], point[C])) continue;
                    for(int D = 0; D < point.size(); ++D) {
                        if(!ccw(point[C], point[D], point[A])) continue;
                        if(dp[A][k - 1][B][C].point.empty()) continue;
                        Polygon next = dp[A][k - 1][B][C];
                        next.point.push_back(D);
                        next.area += area(point[C], point[D], point[A]);
                        if(!ccw(next.point, point)) continue;
                        if(less_polygon(next, dp[A][k][B][D], point)) {
//                             for(auto v: next.point)cout<<" "<<v;cout<<" - "<<next.area<<endl;
                            dp[A][k][B][D] = next;
                            if(less_polygon(next, answer[k], point)) answer[k] = next;
                        }
                    }
                }
            }
        }
    }

    for(const auto& q: query) {
        for(int i = 0; i < answer[q].point.size(); ++i) cout << (i != 0 ? " " : "") << answer[q].point[i] + 1;
        if(answer[q].point.empty()) cout << "NA";
        cout << endl;
    }
}