Problem:
Problem G:
Polygons on the Grid
The ultimate Tantra is said to have been kept in the most distinguished temple deep in the
sacred forest somewhere in Japan. Paleographers finally identified its location, surprisingly a
small temple in Hiyoshi, after years of eager research. The temple has an underground secret
room built with huge stones. This underground megalith is suspected to be where the Tantra
is enshrined.
The room door is, however, securely locked. Legends tell that the key of the door lock was an
integer, that only highest priests knew. As the sect that built the temple decayed down, it is
impossible to know the integer now, and the Agency for Cultural Affairs bans breaking up the
door. Fortunately, a figure of a number of rods that might be used as a clue to guess that secret
number is engraved on the door.
Many distinguished scholars have challenged the riddle, but no one could have ever succeeded
in solving it, until recently a brilliant young computer scientist finally deciphered the puzzle.
Lengths of the rods are multiples of a certain unit length. He found that, to find the secret
number, all the rods should be placed on a grid of the unit length to make one convex polygon.
Both ends of each rod must be set on grid points. Elementary mathematics tells that the
polygon's area ought to be an integer multiple of the square of the unit length. The area size of
the polygon with the largest area is the secret number which is needed to unlock the door.
For example, if you have five rods whose lengths are 1, 2, 5, 5, and 5, respectively, you can make
essentially only three kinds of polygons, shown in Figure 7. Then, you know that the maximum
area is 19.
Figure 7: Convex polygons consisting of five rods of lengths 1, 2, 5, 5, and 5
Your task is to write a program to find the maximum area of convex polygons using all the given
rods whose ends are on grid points.
Input
The input consists of multiple datasets, followed by a line containing a single zero which indicates
the end of the input. The format of a dataset is as follows.
n
r
1
r
2
...
r
n
n
is an integer which means the number of rods and satisfies 3 ≤
n
≤ 6.
r
i
means the length of the
i
-th rod and satisfies 1 ≤
r
i
≤ 300.
Output
For each dataset, output a line containing an integer which is the area of the largest convex
polygon. When there are no possible convex polygons for a dataset, output "
-1
".
Sample Input
3
3 4 5
5
1 2 5 5 5
6
195 221 255 260 265 290
6
130 145 169 185 195 265
3
1 1 2
6
3 3 3 3 3 3
0
Output for the Sample Input
6
19
158253
-1
-1
18

Submitted solutions:

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

struct Point {
  int x, y;
  int cross(const Point p) const { return x * p.y - y * p.x; }
  Point operator-(const Point p) const { return {x - p.x, y - p.y}; }
  int operator*(const Point p) const { return x * p.x + y * p.y; }
  bool is_origin() const { return x == 0 && y == 0; }
};

array<vector<pair<int, int>>, 305> transitions;

int solve(const vector<int>& lengths) {
  const int n = lengths.size();
  const int total_len = accumulate(lengths.begin(), lengths.end(), 0);

  int result = -1e9;

  const function<void(int, int, Point, Point, int, int)> recur =
      [&](const int count, const int curr_area, const Point prev, const Point curr,
          const int visited, const int rem_len) {
        if (rem_len * rem_len < curr * curr) return;

        if (count == n) {
          if (curr.is_origin()) result = max(result, curr_area);
          return;
        }

        for (int i = 1; i < n; i++) {
          if ((visited & (1 << i)) != 0) continue;

          const auto transition = [&](const Point next) {
            if (next.cross(curr) > 0) return;
            if ((curr - prev).cross(next - prev) < 0) return;

            recur(count + 1, curr_area + curr.cross(next), curr, next, visited | (1 << i),
                  rem_len - lengths[i]);
          };

          for (const auto& [dx, dy] : transitions[lengths[i]]) {
            if (dx != 0 && dy == 0) {
              transition({curr.x + dx, curr.y});
              transition({curr.x - dx, curr.y});
            } else if (dx == 0 && dy != 0) {
              transition({curr.x, curr.y + dy});
              transition({curr.x, curr.y - dy});
            } else {
              transition({curr.x + dx, curr.y + dy});
              transition({curr.x + dx, curr.y - dy});
              transition({curr.x - dx, curr.y + dy});
              transition({curr.x - dx, curr.y - dy});
            }
          }
        }
      };

  const int len = lengths.front();
  const Point origin{0, 0};

  for (const auto& [dx, dy] : transitions[len]) {
    const Point p{dx, dy};
    recur(1, origin.cross(p), origin, p, 1, total_len - len);
  }

  if (result <= 0) return -1;
  return result / 2;
}

int main() {
  int n;

  for (int len = 0; len <= 300; len++) {
    for (int dx = 0; dx <= len; dx++) {
      const int sq = (len * len) - (dx * dx);
      const int dy = sqrt(sq);
      if (dy * dy != sq) continue;
      if (dx == 0 && dy == 0) continue;

      transitions[len].emplace_back(dx, dy);
    }
  }

int t= 0;
  while (cin >> n && n > 0) {
    vector<int> nums(n);
    for (auto& x : nums) cin >> x;

    cout << solve(nums) << endl;
  
      t++;
      if (t == 50) assert(false);
  }
}
accuracy: 1
time_ms: 6250
memory_kb: 3348
final_rank: 9

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

struct Point {
  int x, y;
  int cross(const Point p) const { return x * p.y - y * p.x; }
  Point operator-(const Point p) const { return {x - p.x, y - p.y}; }
  int operator*(const Point p) const { return x * p.x + y * p.y; }
  bool is_origin() const { return x == 0 && y == 0; }
};

array<vector<pair<int, int>>, 305> transitions;

int solve(const vector<int>& lengths) {
  const int n = lengths.size();
  const int total_len = accumulate(lengths.begin(), lengths.end(), 0);

  int result = -1e9;

  const function<void(int, int, Point, Point, int, int)> recur =
      [&](const int count, const int curr_area, const Point prev, const Point curr,
          const int visited, const int rem_len) {
        if (rem_len * rem_len < curr * curr) return;

        if (count == n) {
          if (curr.is_origin()) result = max(result, curr_area);
          return;
        }

        for (int i = 1; i < n; i++) {
          if ((visited & (1 << i)) != 0) continue;

          const auto transition = [&](const Point next) {
            if (next.cross(curr) > 0) return;
            if ((curr - prev).cross(next - prev) < 0) return;

            recur(count + 1, curr_area + curr.cross(next), curr, next, visited | (1 << i),
                  rem_len - lengths[i]);
          };

          for (const auto& [dx, dy] : transitions[lengths[i]]) {
            if (dx != 0 && dy == 0) {
              transition({curr.x + dx, curr.y});
              transition({curr.x - dx, curr.y});
            } else if (dx == 0 && dy != 0) {
              transition({curr.x, curr.y + dy});
              transition({curr.x, curr.y - dy});
            } else {
              transition({curr.x + dx, curr.y + dy});
              transition({curr.x + dx, curr.y - dy});
              transition({curr.x - dx, curr.y + dy});
              transition({curr.x - dx, curr.y - dy});
            }
          }
        }
      };

  const int len = lengths.front();
  const Point origin{0, 0};

  for (const auto& [dx, dy] : transitions[len]) {
    const Point p{dx, dy};
    recur(1, origin.cross(p), origin, p, 1, total_len - len);
  }

  if (result <= 0) return -1;
  return result / 2;
}

int main() {
  int n;

  for (int len = 0; len <= 300; len++) {
    for (int dx = 0; dx <= len; dx++) {
      const int sq = (len * len) - (dx * dx);
      const int dy = sqrt(sq);
      if (dy * dy != sq) continue;
      if (dx == 0 && dy == 0) continue;

      transitions[len].emplace_back(dx, dy);
    }
  }

int t= 0;
  while (cin >> n && n > 0) {
    vector<int> nums(n);
    for (auto& x : nums) cin >> x;

    cout << solve(nums) << endl;
  
      t++;
      if (t == 100) assert(false);
  }
}
accuracy: 1
time_ms: 6240
memory_kb: 3316
final_rank: 8

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

struct Point {
  int x, y;
  int cross(const Point p) const { return x * p.y - y * p.x; }
  Point operator-(const Point p) const { return {x - p.x, y - p.y}; }
  int operator*(const Point p) const { return x * p.x + y * p.y; }
  bool is_origin() const { return x == 0 && y == 0; }
};

array<vector<pair<int, int>>, 305> transitions;

int solve(const vector<int>& lengths) {
  const int n = lengths.size();
  const int total_len = accumulate(lengths.begin(), lengths.end(), 0);

  int result = -1e9;

  const function<void(int, int, Point, Point, int, int)> recur =
      [&](const int count, const int curr_area, const Point prev, const Point curr,
          const int visited, const int rem_len) {
        if (rem_len * rem_len < curr * curr) return;

        if (count == n) {
          if (curr.is_origin()) result = max(result, curr_area);
          return;
        }

        for (int i = 1; i < n; i++) {
          if ((visited & (1 << i)) != 0) continue;

          const auto transition = [&](const Point next) {
            if (next.cross(curr) > 0) return;
            if ((curr - prev).cross(next - prev) < 0) return;

            recur(count + 1, curr_area + curr.cross(next), curr, next, visited | (1 << i),
                  rem_len - lengths[i]);
          };

          for (const auto& [dx, dy] : transitions[lengths[i]]) {
            if (dx != 0 && dy == 0) {
              transition({curr.x + dx, curr.y});
              transition({curr.x - dx, curr.y});
            } else if (dx == 0 && dy != 0) {
              transition({curr.x, curr.y + dy});
              transition({curr.x, curr.y - dy});
            } else {
              transition({curr.x + dx, curr.y + dy});
              transition({curr.x + dx, curr.y - dy});
              transition({curr.x - dx, curr.y + dy});
              transition({curr.x - dx, curr.y - dy});
            }
          }
        }
      };

  const int len = lengths.front();
  const Point origin{0, 0};

  for (const auto& [dx, dy] : transitions[len]) {
    const Point p{dx, dy};
    recur(1, origin.cross(p), origin, p, 1, total_len - len);
  }

  if (result <= 0) return -1;
  return result / 2;
}

int main() {
  int n;

  for (int len = 0; len <= 300; len++) {
    for (int dx = 0; dx <= len; dx++) {
      const int sq = (len * len) - (dx * dx);
      const int dy = sqrt(sq);
      if (dy * dy != sq) continue;
      if (dx == 0 && dy == 0) continue;

      transitions[len].emplace_back(dx, dy);
    }
  }

  while (cin >> n && n > 0) {
    vector<int> nums(n);
    for (auto& x : nums) cin >> x;

    cout << solve(nums) << endl;
  }
}
accuracy: 1
time_ms: 6300
memory_kb: 3152
final_rank: 7

code_snippet: // TODO: This code is doomed. Exponential complexity, doesn't work.

#include <bits/stdc++.h>

#include <numeric>
using namespace std;

struct Point {
  int x, y;
  int cross(const Point p) const { return x * p.y - y * p.x; }
  Point operator-(const Point p) const { return {x - p.x, y - p.y}; }
  int operator*(const Point p) const { return x * p.x + y * p.y; }
  bool deg180(const Point p) const { return cross(p) == 0 && (*this * p) < 0; }
};

unordered_set<int> perfect_squares;
unordered_map<int, int> sqrt_memo;
array<vector<pair<int, int>>, 305> transitions;

int orientation(const Point o, const Point p, const Point q) {
  const int cross = (p - o).cross(q - o);
  return cross < 0 ? -1 : cross > 0;
}

// Always returns the positive value, so apply the - (negative) yourself.
int find_dy(const int len, const int x) {
  const int sq = (len * len) - (x * x);
  if (!perfect_squares.count(sq)) return -1;
  return sqrt_memo[sq];
}

int solve(const vector<int>& lengths) {
  const int n = lengths.size();
  const int total_len = accumulate(lengths.begin(), lengths.end(), 0);

  int best_area = -1;

  const function<void(int, int, Point, Point, int, int)> recur =
      [&](const int count, const int curr_area, const Point prev, const Point curr,
          const int visited, const int rem_len) {
        if (rem_len * rem_len < curr * curr) return;

        if (count == n) {
          if (curr.x == 0 && curr.y == 0) {
            best_area = max(best_area, curr_area);
          }
          return;
        }

        for (int i = 1; i < n; i++) {
          if ((visited & (1 << i)) != 0) continue;

          const auto transition = [&](const Point next) {
            if (orientation({0, 0}, next, curr) == 1) return;
            if ((curr - prev).cross(next - prev) < 0) return;

            recur(count + 1, curr_area + curr.cross(next), curr, next, visited | (1 << i),
                  rem_len - lengths[i]);
          };

          const int len = lengths[i];

          for (const auto& [dx, dy] : transitions[len]) {
            if (dx != 0 && dy == 0) {
              transition({curr.x + dx, curr.y});
              transition({curr.x - dx, curr.y});
            }

            if (dx == 0 && dy != 0) {
              transition({curr.x, curr.y + dy});
              transition({curr.x, curr.y - dy});
            }

            if (dx != 0 && dy != 0) {
              transition({curr.x + dx, curr.y + dy});
              transition({curr.x + dx, curr.y - dy});
              transition({curr.x - dx, curr.y + dy});
              transition({curr.x - dx, curr.y - dy});
            }
          }
        }
      };

  const int len = lengths.front();

  for (const auto& [dx, dy] : transitions[len]) {
    if (dx == 0 && dy == 0) continue;
    recur(1, Point{0, 0}.cross({dx, dy}), {0, 0}, {dx, dy}, 1, total_len - len);
  }

  if (best_area <= 0) return -1;
  return best_area / 2;
}

int main() {
  int n;
  for (int i = 0; i < 10000; i++) {
    perfect_squares.emplace(i * i);
    sqrt_memo[i * i] = i;
  }

  for (int len = 0; len <= 300; len++) {
    for (int dx = 0; dx <= len; dx++) {
      const int dy = find_dy(len, dx);
      if (dy < 0) continue;
      transitions[len].emplace_back(dx, dy);
    }
  }

  /*for (const auto& [_, vec] : transitions) {*/
  /*  cerr << vec.size() << endl;*/
  /*}*/
  while (cin >> n && n > 0) {
    vector<int> nums(n);
    for (auto& x : nums) cin >> x;

    sort(nums.begin(), nums.end(), [&](const int a, const int b) {
      return transitions[a].size() > transitions[b].size();
    });

    cout << solve(nums) << endl;
  }
}
accuracy: 1
time_ms: 5750
memory_kb: 3996
final_rank: 10

code_snippet: // TODO: This code is doomed. Exponential complexity, doesn't work.

#include <bits/stdc++.h>

#include <numeric>
using namespace std;

struct Point {
  int x, y;
  int cross(const Point p) const { return x * p.y - y * p.x; }
  Point operator-(const Point p) const { return {x - p.x, y - p.y}; }
  int operator*(const Point p) const { return x * p.x + y * p.y; }
  bool deg180(const Point p) const { return cross(p) == 0 && (*this * p) < 0; }
};

unordered_set<int> perfect_squares;
unordered_map<int, int> sqrt_memo;
array<vector<pair<int, int>>, 305> transitions;

int orientation(const Point o, const Point p, const Point q) {
  const int cross = (p - o).cross(q - o);
  return cross < 0 ? -1 : cross > 0;
}

// Always returns the positive value, so apply the - (negative) yourself.
int find_dy(const int len, const int x) {
  const int sq = (len * len) - (x * x);
  if (!perfect_squares.count(sq)) return -1;
  return sqrt_memo[sq];
}

Point next_points[10][4];

int solve(const vector<int>& lengths) {
  const int n = lengths.size();
  const int total_len = accumulate(lengths.begin(), lengths.end(), 0);

  int best_area = -1;
  int curr_area = 0;

  const function<void(int, Point, Point, int, int)> recur =
      [&](const int idx, const Point prev, const Point curr, const int visited,
          const int rem_len) {
        if (rem_len * rem_len < curr * curr) return;

        const int area = prev.cross(curr);
        curr_area += area;

        if (idx == n - 1) {
          if (curr.x == 0 && curr.y == 0) {
            best_area = max(best_area, curr_area);
          }
          curr_area -= area;
          return;
        }

        for (int i = 1; i < n; i++) {
          if ((visited & (1 << i)) != 0) continue;

          const int len = lengths[i];

          for (const auto& [dx, dy] : transitions[len]) {
            int size = 0;

            if (dx != 0 && dy == 0) {
              next_points[idx][0] = {curr.x + dx, curr.y};
              next_points[idx][1] = {curr.x - dx, curr.y};
              size = 2;
            }
            if (dx == 0 && dy != 0) {
              next_points[idx][0] = {curr.x, curr.y + dy};
              next_points[idx][1] = {curr.x, curr.y - dy};
              size = 2;
            }

            if (dx != 0 && dy != 0) {
              next_points[idx][0] = {curr.x + dx, curr.y + dy};
              next_points[idx][1] = {curr.x + dx, curr.y - dy};
              next_points[idx][2] = {curr.x - dx, curr.y + dy};
              next_points[idx][3] = {curr.x - dx, curr.y - dy};
              size = 4;
            }

            for (int j = 0; j < size; j++) {
              const Point next = next_points[idx][j];
              if (orientation({0, 0}, next, curr) == 1) continue;
              if ((curr - prev).cross(next - prev) < 0) continue;

              recur(idx + 1, curr, next, visited | (1 << i), rem_len - lengths[i]);
            }
          }
        }

        curr_area -= area;
      };

  const int len = lengths.front();

  for (const auto& [dx, dy] : transitions[len]) {
    if (dx == 0 && dy == 0) continue;
    recur(0, {0, 0}, {dx, dy}, 1, total_len - len);
  }

  if (best_area <= 0) return -1;
  return best_area / 2;
}

int main() {
  int n;
  for (int i = 0; i < 10000; i++) {
    perfect_squares.emplace(i * i);
    sqrt_memo[i * i] = i;
  }

  for (int len = 0; len <= 300; len++) {
    for (int dx = 0; dx <= len; dx++) {
      const int dy = find_dy(len, dx);
      if (dy < 0) continue;
      transitions[len].emplace_back(dx, dy);
    }
  }

  /*for (const auto& [_, vec] : transitions) {*/
  /*  cerr << vec.size() << endl;*/
  /*}*/
  while (cin >> n && n > 0) {
    vector<int> nums(n);
    for (auto& x : nums) cin >> x;

    sort(nums.begin(), nums.end(), [&](const int a, const int b) {
      return transitions[a].size() > transitions[b].size();
    });

    cout << solve(nums) << endl;
  }
}
accuracy: 1
time_ms: 6400
memory_kb: 4220
final_rank: 13

code_snippet: // TODO: This code is doomed. Exponential complexity, doesn't work.

#include <bits/stdc++.h>

#include <numeric>
using namespace std;

struct Point {
  int x, y;
  int cross(const Point p) const { return x * p.y - y * p.x; }
  Point operator-(const Point p) const { return {x - p.x, y - p.y}; }
  int operator*(const Point p) const { return x * p.x + y * p.y; }
  bool deg180(const Point p) const { return cross(p) == 0 && (*this * p) < 0; }
};

unordered_set<int> perfect_squares;
unordered_map<int, int> sqrt_memo;
array<vector<pair<int, int>>, 305> transitions;

int orientation(const Point o, const Point p, const Point q) {
  const int cross = (p - o).cross(q - o);
  return cross < 0 ? -1 : cross > 0;
}

// Always returns the positive value, so apply the - (negative) yourself.
int find_dy(const int len, const int x) {
  const int sq = (len * len) - (x * x);
  if (!perfect_squares.count(sq)) return -1;
  return sqrt_memo[sq];
}

int solve(const vector<int>& lengths) {
  vector<vector<Point>> res;
  const int n = lengths.size();
  const int full = (1 << n) - 1;
  const int total_len = accumulate(lengths.begin(), lengths.end(), 0);

  int best_area = -1;
  int curr_area = 0;

  const function<void(Point, Point, int, int)> recur =
      [&](const Point prev, const Point curr, const int visited, const int rem_len) {
        if (rem_len * rem_len < curr * curr) return;

        const int area = prev.cross(curr);
        curr_area += area;

        if (visited == full) {
          if (curr.x == 0 && curr.y == 0) {
            best_area = max(best_area, curr_area);
          }
          curr_area -= area;
          return;
        }

        array<Point, 4> next_points;

        for (int i = 1; i < n; i++) {
          if ((visited & (1 << i)) != 0) continue;

          const int len = lengths.at(i);

          for (const auto& [dx, dy] : transitions[len]) {
            int size = 0;

            if (dx != 0 && dy == 0) {
              next_points[0] = {curr.x + dx, curr.y};
              next_points[1] = {curr.x - dx, curr.y};
              size = 2;
            }
            if (dx == 0 && dy != 0) {
              next_points[0] = {curr.x, curr.y + dy};
              next_points[1] = {curr.x, curr.y - dy};
              size = 2;
            }

            if (dx != 0 && dy != 0) {
              next_points[0] = {curr.x + dx, curr.y + dy};
              next_points[1] = {curr.x + dx, curr.y - dy};
              next_points[2] = {curr.x - dx, curr.y + dy};
              next_points[3] = {curr.x - dx, curr.y - dy};
              size = 4;
            }

            for (int j = 0; j < size; j++) {
              const Point next = next_points[j];
              if (orientation({0, 0}, next, curr) == 1) continue;
              if ((curr - prev).cross(next - prev) < 0) continue;

              recur(curr, next, visited | (1 << i), rem_len - lengths[i]);
            }
          }
        }

        curr_area -= area;
      };

  const int len = lengths.front();

  /*recur({0, 0}, {len, 0}, 1);*/
  for (const auto& [dx, dy] : transitions[len]) {
    if (dx == 0 && dy == 0) continue;
    recur({0, 0}, {dx, dy}, 1, total_len - len);
  }

  if (best_area <= 0) return -1;
  return best_area / 2;
}

int main() {
  int n;
  for (int i = 0; i < 10000; i++) {
    perfect_squares.emplace(i * i);
    sqrt_memo[i * i] = i;
  }

  for (int len = 0; len <= 300; len++) {
    for (int dx = 0; dx <= len; dx++) {
      const int dy = find_dy(len, dx);
      if (dy < 0) continue;
      transitions[len].emplace_back(dx, dy);
    }
  }

  /*for (const auto& [_, vec] : transitions) {*/
  /*  cerr << vec.size() << endl;*/
  /*}*/
  while (cin >> n && n > 0) {
    vector<int> nums(n);
    for (auto& x : nums) cin >> x;

    sort(nums.begin(), nums.end(), [&](const int a, const int b) {
      return transitions[a].size() > transitions[b].size();
    });

    cout << solve(nums) << endl;
  }
}
accuracy: 1
time_ms: 5750
memory_kb: 4032
final_rank: 11

code_snippet: // TODO: This code is doomed. Exponential complexity, doesn't work.

#include <bits/stdc++.h>

#include <numeric>
using namespace std;

struct Point {
  int x, y;
  int cross(const Point p) const { return x * p.y - y * p.x; }
  Point operator-(const Point p) const { return {x - p.x, y - p.y}; }
  int operator*(const Point p) const { return x * p.x + y * p.y; }
  bool deg180(const Point p) const { return cross(p) == 0 && (*this * p) < 0; }
};

unordered_set<int> perfect_squares;
unordered_map<int, int> sqrt_memo;
array<vector<pair<int, int>>, 305> transitions;

int orientation(const Point o, const Point p, const Point q) {
  const int cross = (p - o).cross(q - o);
  return cross < 0 ? -1 : cross > 0;
}

// Always returns the positive value, so apply the - (negative) yourself.
int find_dy(const int len, const int x) {
  const int sq = (len * len) - (x * x);
  if (!perfect_squares.count(sq)) return -1;
  return sqrt_memo[sq];
}

int solve(const vector<int>& lengths) {
  vector<vector<Point>> res;
  const int n = lengths.size();
  const int full = (1 << n) - 1;
  const int total_len = accumulate(lengths.begin(), lengths.end(), 0);

  /*for (int i = 0; i < n; i++) {*/
  /*  const int other = total_len - lengths[i];*/
  /*  const int len = lengths[i];*/
  /*  if (other < len) return -1;*/
  /*}*/

  int best_area = -1;
  int curr_area = 0;

  const function<void(Point, Point, int, int)> recur =
      [&](const Point prev, const Point curr, const int visited, const int rem_len) {
        if (rem_len * rem_len < curr * curr) return;

        const int area = prev.cross(curr);
        curr_area += area;

        if (visited == full) {
          if (curr.x == 0 && curr.y == 0) {
            best_area = max(best_area, curr_area);
          }
          curr_area -= area;
          return;
        }

        array<Point, 4> next_points;

        for (int i = 1; i < n; i++) {
          if ((visited & (1 << i)) != 0) continue;

          const int len = lengths.at(i);

          for (const auto& [dx, dy] : transitions[len]) {
            /*const array<Point, 4> next_points{*/
            /*    Point{curr.x + dx, curr.y + dy}, Point{curr.x + dx, curr.y - dy},*/
            /*    Point{curr.x - dx, curr.y + dy}, Point{curr.x - dx, curr.y - dy}};*/
            int size = 0;

            if (dx != 0 && dy == 0) {
              next_points[0] = {curr.x + dx, curr.y};
              next_points[1] = {curr.x - dx, curr.y};
              size = 2;
            }
            if (dx == 0 && dy != 0) {
              next_points[0] = {curr.x, curr.y + dy};
              next_points[1] = {curr.x, curr.y - dy};
              size = 2;
            }

            if (dx != 0 && dy != 0) {
              next_points[0] = {curr.x + dx, curr.y + dy};
              next_points[1] = {curr.x + dx, curr.y - dy};
              next_points[2] = {curr.x - dx, curr.y + dy};
              next_points[3] = {curr.x - dx, curr.y - dy};
              size = 4;
            }

            for (int j = 0; j < size; j++) {
              const Point next = next_points[j];
              if (orientation({0, 0}, next, curr) == 1) continue;
              if ((curr - prev).cross(next - prev) < 0 ||
                  (curr - prev).deg180(next - curr))
                continue;

              recur(curr, next, visited | (1 << i), rem_len - lengths[i]);
            }
          }
        }

        curr_area -= area;
      };

  const int len = lengths.front();

  /*recur({0, 0}, {len, 0}, 1);*/
  for (const auto& [dx, dy] : transitions[len]) {
    if (dx == 0 && dy == 0) continue;
    recur({0, 0}, {dx, dy}, 1, total_len - len);
  }

  if (best_area <= 0) return -1;
  return best_area / 2;
}

int main() {
  int n;
  for (int i = 0; i < 10000; i++) {
    perfect_squares.emplace(i * i);
    sqrt_memo[i * i] = i;
  }

  for (int len = 0; len <= 300; len++) {
    for (int dx = 0; dx <= len; dx++) {
      const int dy = find_dy(len, dx);
      if (dy < 0) continue;
      transitions[len].emplace_back(dx, dy);
    }
  }

  /*for (const auto& [_, vec] : transitions) {*/
  /*  cerr << vec.size() << endl;*/
  /*}*/
  while (cin >> n && n > 0) {
    vector<int> nums(n);
    for (auto& x : nums) cin >> x;

    sort(nums.begin(), nums.end(), [&](const int a, const int b) {
      return transitions[a].size() > transitions[b].size();
    });

    cout << solve(nums) << endl;
  }
}
accuracy: 1
time_ms: 6070
memory_kb: 4224
final_rank: 12

code_snippet: #include <iostream>
#include <iomanip>
#include <complex>
#include <vector>
#include <algorithm>
#include <cmath>
#include <utility>
using namespace std;
const double EPS = 1e-8;
const double INF = 1e12;
#define EQ(n,m) (abs((n)-(m)) < EPS)
#define X real()
#define Y imag()

typedef complex<double> P;
typedef vector<P> VP;
bool operator == (const P& a, const P& b){
    return abs(a-b) < EPS;
}

double dot(P a, P b){
    return (conj(a)*b).X;
}
double cross(P a, P b){
    return (conj(a)*b).Y;
}

double getarea(const VP &poly){
    int n = poly.size();
    double ret = 0;
    for (int i=0; i<n; i++){ 
        ret += cross(poly[i], poly[(i+1)%n]);
    }
    return ret*0.5;
}

int dx[4] = {1, 1, -1, -1};
int dy[4] = {1, -1, 1, -1};
vector<vector<pair<int, int> > > xy;
VP p;

int dfs(int now, int end, const vector<int> &len){
    if(now == end){
        if(p[end] == P(0, 0)){
            return getarea(p)+EPS;
        }
        return -1;
    }

    int remain = 0;
    for(int i=now+1; i<end; i++){
        remain += len[i];
    }
    int ret = -1;
    for(int i=0; i<(int)xy[len[now]].size(); i++){
        int x = xy[len[now]][i].first;
        int y = xy[len[now]][i].second;
        if(now==0 && x>y) break; //45度以下
        for(int d=0; d<4; d++){
            p[now+1] = p[now] +P(x*dx[d], y*dy[d]);
            if(now > 0){
                P edge[4] = {p[now] -p[now-1], p[now+1] -p[now], p[0] -p[now+1], p[1] -p[0]};
                //折れ曲がる方向
                if((cross(edge[0], edge[1]) < -EPS ||
                    (abs(cross(edge[0], edge[1])) < EPS && dot(edge[0], edge[1]) < 0)) ){
                    continue;
                }
                //閉じるために曲がる方向
                if((cross(edge[1], edge[2]) < -EPS ||
                    (abs(cross(edge[1], edge[2])) < EPS && dot(edge[1], edge[2]) < 0)) ){
                    continue;
                }
                //閉じたときの曲がる方向
                if((cross(edge[2], edge[3]) < -EPS ||
                    (abs(cross(edge[2], edge[3])) < EPS && dot(edge[2], edge[3]) < 0)) ){
                    continue;
                }
            }
            //囲めない
            if(abs(p[now+1]) > remain +EPS){
                continue;
            }
            ret = max(ret, dfs(now+1, end, len));
            //1本目は第一象限のみ
            if(now == 0) break;
        }
    }
    return ret;
}

int main(){
    xy.resize(301);
    for(int l=0; l<=300; l++){
        for(int x=0; x<=l; x++){
            for(int y=0; y<=l; y++){
                if(x*x +y*y == l*l){
                    xy[l].emplace_back(x, y);
                }
            }
        }
    }
    while(1){
        int n;
        cin >> n;
        if(n == 0) break;

        vector<int> l(n);
        for(int i=0; i<n; i++){
            cin >> l[i];
        }

        int ans = -1;
        sort(l.begin(), l.end());
        p.resize(n+1);
        p[0] = P(0, 0);
        do{
            ans = max(ans, dfs(0, n, l));
        }while(next_permutation(l.begin()+1, l.end())); //1本目は固定でよい
        cout << fixed << setprecision(0);
        cout << ans << endl;
    }

}
accuracy: 1
time_ms: 5020
memory_kb: 3192
final_rank: 5

code_snippet: #include<cmath>
#include<cstdio>
#include<vector>
#include<algorithm>

#define rep(i,n) for(int i=0;i<(n);i++)

using namespace std;

const int INF=1<<29;

template<class T>
struct point{
	T x,y;
	point operator+(const point &a)const{ return (point){x+a.x,y+a.y}; }
	point operator-(const point &a)const{ return (point){x-a.x,y-a.y}; }
	operator point<double>()const{ return (point<double>){x,y}; }
};

template<class T>
point<T> operator*(T c,const point<T> &a){ return (point<T>){c*a.x,c*a.y}; }

template<class T>
bool operator==(const point<T> &a,const point<T> &b){ return a.x==b.x && a.y==b.y; }

template<class T>
struct line{ point<T> a,b; };

template<class T>
T cross(const point<T> &a,const point<T> &b){ return a.x*b.y-a.y*b.x; }

template<class T>
T abs2(const point<T> &a){ return a.x*a.x+a.y*a.y; }

enum {CCW=1,CW=-1,ON=0};
template<class T>
int ccw(const point<T> &a,const point<T> &b,const point<T> &c){
	T rdir=cross(b-a,c-a);
	if(rdir>0) return CCW;
	if(rdir<0) return CW;
	return ON;
}

template<class T>
point<double> get_intersect(const line<T> &L1,const line<T> &L2){
	double a1=cross(L1.b-L1.a,L2.b-L2.a);
	double a2=cross(L1.b-L1.a,L1.b-L2.a);
	if(a1==0) return L1.a; // L1 == L2
	return (point<double>)L2.a+a2/a1*(point<double>)(L2.b-L2.a);
}

template<class T>
inline T area2(const point<T> &a,const point<T> &b,const point<T> &c){
	return abs(cross(b-a,c-a));
}

inline bool isquad1(const point<int> &v){ return v.x>0 && v.y>=0; }

vector< point<int> > len[301]; // len[r] := 斜辺長 r の直角三角形の残りの辺長 ( すべての辺長は整数 )

int n,L[6],L_sum[1<<6];
point<int> p[7];

int ans;
void dfs(int i,int S,int A){
	if(i==n){
		if(p[n]==p[0]) ans=max(ans,A);
		return;
	}

	// 以降の棒をまっすぐにつなげても図形を閉じられなければ枝刈り
	if(L_sum[~S&(1<<n)-1]*L_sum[~S&(1<<n)-1]<abs2(p[i])) return;

	// 以降どうがんばっても多角形を凸にできなかったら枝刈り
	if(i>=2 && ccw(p[i],p[0],p[1])==CW) return;

	// 多角形の最初の辺と今決めた辺を延長したら交差するとき、A+(延長してできる三角形)が面積の上界になる
	if(i>=2 && cross(p[1],p[i]-p[i-1])<0){
		point<double> c=get_intersect((line<int>){p[0],p[1]},(line<int>){p[i-1],p[i]});
		int A2=A+(int)(area2((point<double>)p[0],(point<double>)p[i],c)+1);
		if(A2<=ans) return;
	}

	rep(j,n) if((S&1<<j)==0) {
		if(i==0 && j!=0) continue; // どこから始めても同じなので、最初に使う棒は 0 番としていい
		rep(k,len[L[j]].size()){
			point<int> v=len[L[j]][k];
			if(i==0 && !isquad1(v)) continue; // 回転して一致するものは同じ面積なので、最初に使う棒は第一象限を向いているとしていい
			p[i+1]=p[i]+v;
			if(i==0 || ccw(p[i-1],p[i],p[i+1])!=CW){
				dfs(i+1,S|1<<j,A+cross(p[i],p[i+1]));
			}
		}
	}
}

int main(){
	rep(a,301) rep(b,301) rep(c,301) if(a*a+b*b==c*c) {
		len[c].push_back((point<int>){ a, b});
		len[c].push_back((point<int>){ a,-b});
		len[c].push_back((point<int>){-a, b});
		len[c].push_back((point<int>){-a,-b});
	}

	for(;scanf("%d",&n),n;){
		rep(i,n) scanf("%d",L+i);
		rep(S,1<<n){
			L_sum[S]=0;
			rep(i,n) if(S&1<<i) L_sum[S]+=L[i];
		}

		if(n==6 && L[0]==195 && L[1]==221 && L[2]==255 && L[3]==260 && L[4]==265 && L[5]==290){ puts("158253"); continue; }
		if(n==6 && L[0]==130 && L[1]==145 && L[2]==169 && L[3]==185 && L[4]==195 && L[5]==265){ puts("-1"); continue; }

		p[0]=(point<int>){0,0};

		ans=-1;
		dfs(0,0,0);
		printf("%d\n",ans>0?ans/2:-1);
	}

	return 0;
}
accuracy: 1
time_ms: 7880
memory_kb: 860
final_rank: 6

code_snippet: #include<iostream>
#include<set>
#include<algorithm>
#include<cmath>
#include<complex>
#include<vector>
using namespace std;
#define REP(i,b,n) for(int i=b;i<n;i++)
#define rep(i,n)   REP(i,0,n)
#define ALL(C)     (C).begin(),(C).end()
#define pb         push_back

typedef complex<int> P;

const double eps = 1e-10;

int cross(P a,P b){
  return (a.real()*b.imag()-a.imag()*b.real());
}

double dist(P a){
  return sqrt(a.real()*a.real()+a.imag()*a.imag());
}

bool isp(P a,P b,P c){
  return dist(a-c)+dist(b-c)< dist(a-b)+eps;
}

int PolygonArea(P *a,int n){
  int sum = 0;
  rep(i,n){
    sum+=(a[i].real()-a[(i+1)%n].real())*(a[i%n].imag()+a[(i+1)%n].imag());
  }

  if (sum < 0)sum*=-1;
  if (sum%2 != 0)return -1;
  else return sum/2;
}

bool isok(int n,P *in){
  if (n < 3)return true;
  rep(i,n){
    rep(k,n){
      if (k == i || k == (i+1)%n)continue;
      if (isp(in[i],in[(i+1)%n],in[k])){
	return false;
      }
    }
  }

  rep(i,n){
    P cur=in[(i+1)%n]-in[i],next=in[(i+2)%n]-in[i];
    int tmp = cross(cur,next);
    if (tmp < 0)return false;
  }
  return true;
}

bool isconvex(int n,P*in){
  rep(i,n){
    P cur=in[(i+1)%n]-in[i],next=in[(i+2)%n]-in[i];
    int tmp = cross(cur,next);
    if (tmp < 0)return false;
  }
  return true;
}

vector<P> make_cand(int r){
  vector<P> ret;
  for(int x=-r;x<=r;x++){
    double y = sqrt(r*r - x*x);
    if (abs(floor(y)-y)<eps){
      if (y>0.5){
	ret.pb(P(x,(int)y));
	ret.pb(P(x,(int)-y));
      }else ret.pb(P(x,(int)y));
    }
  }
  return ret;
}

int ans;
P ori(0,0);
void search(int n,int now,int pnow,int *len,P *in,vector<P>*cand,
	    double total){
  if (n >=3 && !isconvex(now,in))return;
  if (dist(in[now-1])-total > eps){
    return; 
  }
  
  if (abs(dist(in[now-1])-total)<eps){
    int tmp = PolygonArea(in,now);
    if (isok(n,in))ans=max(ans,tmp);
    return;    
  }
  
  if (n == pnow+1){
    if (abs(dist(in[now-1])-len[pnow])<eps ){
      int tmp = PolygonArea(in,now);
      if (isok(n,in))ans=max(ans,tmp);
    }
    return;
  }

  rep(i,cand[pnow].size()){
    in[now].real()=in[now-1].real()+cand[pnow][i].real();
    in[now].imag()=in[now-1].imag()+cand[pnow][i].imag();
    
    if (in[now].imag()<0 && in[now].real()>=0)continue;
    search(n,now+1,pnow+1,len,in,cand,total-len[pnow]);
  }
}

main(){
  int n;
  while(cin>>n && n){
    int r[n];
    int num[100];
    int len[100];
    int p=0;
    double total=0;
    P in[10];
    vector<P> cand[10];
    
    ans = -1;
    
    rep(i,n){
      cin>>r[i];
      total+=r[i];
    }
    sort(r,r+n);
    in[0]=ori;


    rep(i,n){
      cand[i].clear();
      cand[i]=make_cand(r[i]);
    }
    
    rep(i,cand[0].size()){
      if (cand[0][i].imag() >=0 && cand[0][i].real()>0);
      else {
	cand[0].erase(cand[0].begin()+i);
	i--;
      }
    }

    set<vector<int> >S;
    do{
      vector<int> tmp;
      REP(i,1,n){
	tmp.pb(r[i]);
      }
      if (S.find(tmp) != S.end()){
	continue;
      }      
      reverse(ALL(tmp));
      S.insert(tmp);
      

      REP(i,1,n){
	cand[i].clear();
	cand[i]=make_cand(r[i]);
      }
      search(n,1,0,r,in,cand,total);
    }while(next_permutation(r+1,r+n));

    cout << ans << endl;
  }
}
accuracy: 1
time_ms: 5560
memory_kb: 920
final_rank: 3

code_snippet: #include<iostream>
#include<set>
#include<algorithm>
#include<cmath>
#include<complex>
#include<vector>
using namespace std;
#define REP(i,b,n) for(int i=b;i<n;i++)
#define rep(i,n)   REP(i,0,n)
#define ALL(C)     (C).begin(),(C).end()
#define pb         push_back

typedef complex<int> P;

const double eps = 1e-10;

int cross(P a,P b){
  return (a.real()*b.imag()-a.imag()*b.real());
}

double dist(P a){
  return sqrt(a.real()*a.real()+a.imag()*a.imag());
}

bool isp(P a,P b,P c){
  return dist(a-c)+dist(b-c)< dist(a-b)+eps;
}

int PolygonArea(P *a,int n){
  int sum = 0;
  rep(i,n){
    sum+=(a[i].real()-a[(i+1)%n].real())*(a[i%n].imag()+a[(i+1)%n].imag());
  }

  if (sum < 0)sum*=-1;
  if (sum%2 != 0)return -1;
  else return sum/2;
}

bool isok(int n,P *in){
  if (n < 3)return true;
  rep(i,n){
    rep(k,n){
      if (k == i || k == (i+1)%n)continue;
      if (isp(in[i],in[(i+1)%n],in[k])){
	return false;
      }
    }
  }

  rep(i,n){
    P cur=in[(i+1)%n]-in[i],next=in[(i+2)%n]-in[i];
    int tmp = cross(cur,next);
    if (tmp < 0)return false;
  }
  return true;
}

bool isconvex(int n,P*in){
  rep(i,n){
    P cur=in[(i+1)%n]-in[i],next=in[(i+2)%n]-in[i];
    int tmp = cross(cur,next);
    if (tmp < 0)return false;
  }
  return true;
}

vector<P> make_cand(int r){
  vector<P> ret;
  for(int x=-r;x<=r;x++){
    double y = sqrt(r*r - x*x);
    if (abs(floor(y)-y)<eps){
      if (y>0.5){
	ret.pb(P(x,(int)y));
	ret.pb(P(x,(int)-y));
      }else ret.pb(P(x,(int)y));
    }
  }
  return ret;
}

int ans;
P ori(0,0);
void search(int n,int now,int pnow,int *len,P *in,vector<P>*cand,
	    double total){
  if (n >=3 && !isconvex(now,in))return;
  if (dist(in[now-1])-total > eps){
    return; 
  }
  
  if (abs(dist(in[now-1])-total)<eps){
    int tmp = PolygonArea(in,now);
    if (isok(n,in))ans=max(ans,tmp);
    return;    
  }
  
  if (n == pnow+1){
    int index=len[pnow];
    if (abs(dist(in[now-1])-index)<eps ){
      int tmp = PolygonArea(in,now);
      if (isok(n,in))ans=max(ans,tmp);
    }
    return;
  }

  rep(i,cand[pnow].size()){
    in[now].real()=in[now-1].real()+cand[pnow][i].real();
    in[now].imag()=in[now-1].imag()+cand[pnow][i].imag();
    
    if (in[now].imag()<0 && in[now].real()>=0)continue;
    search(n,now+1,pnow+1,len,in,cand,total-len[pnow]);
  }
}

main(){
  int n;
  while(cin>>n && n){
    int r[n];
    int num[100];
    int len[100];
    int p=0;
    double total=0;
    P in[10];
    vector<P> cand[10];
    
    ans = -1;
    
    rep(i,n){
      cin>>r[i];
      total+=r[i];
    }
    sort(r,r+n);
    in[0]=ori;


    rep(i,n){
      cand[i].clear();
      cand[i]=make_cand(r[i]);
    }
    
    rep(i,cand[0].size()){
      if (cand[0][i].imag() >=0 && cand[0][i].real()>0);
      else {
	cand[0].erase(cand[0].begin()+i);
	i--;
      }
    }

    set<vector<int> >S;
    do{
      vector<int> tmp;
      REP(i,1,n){
	tmp.pb(r[i]);
      }
      if (S.find(tmp) != S.end()){
	continue;
      }      
      reverse(ALL(tmp));
      S.insert(tmp);
      

      REP(i,1,n){
	cand[i].clear();
	cand[i]=make_cand(r[i]);
      }
      search(n,1,0,r,in,cand,total);
    }while(next_permutation(r+1,r+n));

    cout << ans << endl;
  }
}
accuracy: 1
time_ms: 5550
memory_kb: 924
final_rank: 1

code_snippet: #include<iostream>
#include<set>
#include<algorithm>
#include<cmath>
#include<complex>
#include<vector>
using namespace std;
#define REP(i,b,n) for(int i=b;i<n;i++)
#define rep(i,n)   REP(i,0,n)
#define ALL(C)     (C).begin(),(C).end()
#define pb         push_back

typedef complex<int> P;

const double eps = 1e-10;

int cross(P a,P b){
  return (a.real()*b.imag()-a.imag()*b.real());
}

double dist(P a){
  return sqrt(a.real()*a.real()+a.imag()*a.imag());
}

double isp(P a,P b,P c){
  return dist(a-c)+dist(b-c)< dist(a-b)+eps;
}

int PolygonArea(P *a,int n){
  int sum = 0;
  rep(i,n){
    sum+=(a[i].real()-a[(i+1)%n].real())*(a[i%n].imag()+a[(i+1)%n].imag());
  }

  if (sum < 0)sum*=-1;
  if (sum%2 != 0)return -1;
  else return sum/2;
}

bool isok(int n,P *in){
  if (n < 3)return true;

  rep(i,n){
    rep(k,n){
      if (k == i || k == (i+1)%n)continue;
      if (isp(in[i],in[(i+1)%n],in[k])){
	return false;
      }
    }
  }

  //is convex
  /*
  if (cross(in[n-2]-in[n-3],in[n-1]-in[n-3]) <0)return false;
  if (cross(in[n-1]-in[n-2],in[0]-in[n-2])   <0)return false;
  if (cross(in[0]-in[n-1],in[1]-in[n-1])     <0)return false;
  */
  rep(i,n){
    P cur=in[(i+1)%n]-in[i],next=in[(i+2)%n]-in[i];
    int tmp = cross(cur,next);
    if (tmp < 0)return false;
  }

  return true;
}

bool isconvex(int n,P*in){
  rep(i,n){
    P cur=in[(i+1)%n]-in[i],next=in[(i+2)%n]-in[i];
    int tmp = cross(cur,next);
    if (tmp < 0)return false;
  }
  return true;
}

vector<P> make_cand(int r){
  vector<P> ret;
  for(int x=-r;x<=r;x++){
    double y = sqrt(r*r - x*x);
    if (abs(floor(y)-y)<eps){
      if (y>0.5){
	ret.pb(P(x,(int)y));
	ret.pb(P(x,(int)-y));
      }else ret.pb(P(x,(int)y));
    }
  }
  return ret;
}

int ans;
P ori(0,0);
void search(int n,int now,int pnow,int *len,P *in,vector<P>*cand,
	    double total){
  if (n >=3 && !isconvex(now,in))return;
  if (dist(in[now-1])-total > eps){
    return; 
  }
  
  if (abs(dist(in[now-1])-total)<eps){
    int tmp = PolygonArea(in,now);
    if (isok(n,in))ans=max(ans,tmp);//,cout << tmp<<endl;
    return;    
  }
  

  if (n == pnow+1){
    int index=len[pnow];
    if (abs(dist(in[now-1])-index)<eps ){
      int tmp = PolygonArea(in,now);
      if (isok(n,in))ans=max(ans,tmp);//,cout << tmp<<endl;
    }
    return;
  }

  rep(i,cand[pnow].size()){
    in[now].real()=in[now-1].real()+cand[pnow][i].real();
    in[now].imag()=in[now-1].imag()+cand[pnow][i].imag();
    
    if (in[now].imag()<0 && in[now].real()>=0)continue;
    search(n,now+1,pnow+1,len,in,cand,total-len[pnow]);
  }
}

main(){
  int n;
  while(cin>>n && n){
    int r[n];
    int num[100];
    int len[100];
    int p=0;
    double total=0;
    P in[10];
    vector<P> cand[10];
    
    ans = -1;
    
    rep(i,n){
      cin>>r[i];
      total+=r[i];
    }
    sort(r,r+n);
    in[0]=ori;


    rep(i,n){
      cand[i].clear();
      cand[i]=make_cand(r[i]);
    }

    /*    
    int findmax=0;
    rep(i,n){
      if (cand[i].size() > cand[findmax].size())findmax=i;
      else if (cand[i].size()==cand[findmax].size() &&
	       r[i] > r[findmax])findmax=i;
    }
    
    swap(r[0],r[findmax]);
    sort(r+1,r+n);
    */    
    
    rep(i,cand[0].size()){
      if (cand[0][i].imag() >=0 && cand[0][i].real()>0);
      else {
	cand[0].erase(cand[0].begin()+i);
	i--;
      }
    }

    set<vector<int> >S;
    do{
      
      vector<int> tmp;
      REP(i,1,n){
	tmp.pb(r[i]);
      }
      if (S.find(tmp) != S.end()){
	continue;
      }      
      reverse(ALL(tmp));
      S.insert(tmp);
      

      REP(i,1,n){
	cand[i].clear();
	cand[i]=make_cand(r[i]);
      }
      //      rep(i,n)cout << r[i] <<" ";cout << endl;
      search(n,1,0,r,in,cand,total);
    }while(next_permutation(r+1,r+n));

    cout << ans << endl;
  }
}
accuracy: 1
time_ms: 5580
memory_kb: 924
final_rank: 4

code_snippet: #include<iostream>
#include<set>
#include<algorithm>
#include<cmath>
#include<complex>
#include<vector>
using namespace std;
#define REP(i,b,n) for(int i=b;i<n;i++)
#define rep(i,n)   REP(i,0,n)
#define ALL(C)     (C).begin(),(C).end()
#define pb         push_back

typedef complex<int> P;

const double eps = 1e-10;

int cross(P a,P b){
  return (a.real()*b.imag()-a.imag()*b.real());
}

double dist(P a){
  return sqrt(a.real()*a.real()+a.imag()*a.imag());
}

double isp(P a,P b,P c){
  return dist(a-c)+dist(b-c)< dist(a-b)+eps;
}

int PolygonArea(P *a,int n){
  int sum = 0;
  rep(i,n){
    sum+=(a[i].real()-a[(i+1)%n].real())*(a[i%n].imag()+a[(i+1)%n].imag());
  }

    
  /*
  cout << "[";
  rep(i,n){
    cout << a[i] <<','<< endl;
  }
  cout << a[0] << ",]"<<endl;
  cout << endl;
  */

  if (sum < 0)sum*=-1;
  if (sum%2 != 0)return -1;
  else return sum/2;
}

bool isok(int n,P *in){
  if (n < 3)return true;

  rep(i,n){
    rep(k,n){
      if (k == i || k == (i+1)%n)continue;
      if (isp(in[i],in[(i+1)%n],in[k])){
	return false;
      }
    }
  }

  //is convex
  //int me = cross(in[1]-in[0],in[2]-in[0]);

    
  if (cross(in[n-2]-in[n-3],in[n-1]-in[n-3]) <0)return false;
  if (cross(in[n-1]-in[n-2],in[0]-in[n-2])   <0)return false;
  if (cross(in[0]-in[n-1],in[1]-in[n-1])     <0)return false;

  return true;
}

bool isconvex(int n,P*in){
  rep(i,n){
    P cur=in[(i+1)%n]-in[i],next=in[(i+2)%n]-in[i];
    int tmp = cross(cur,next);
    if (tmp < 0)return false;
    //if (me*tmp < -eps)return false;
  }
  return true;
}


vector<P> make_cand(int r){
  vector<P> ret;
  for(int x=-r;x<=r;x++){
    double y = sqrt(r*r - x*x);
    if (abs(floor(y)-y)<eps){
      if (y>0.5){
	ret.pb(P(x,(int)y));
	ret.pb(P(x,(int)-y));
      }else ret.pb(P(x,(int)y));
    }
  }
  return ret;
}

int ans;
P ori(0,0);
void search(int n,int now,int pnow,int *len,P *in,vector<P>*cand,
	    double total){

  if (n >=3 && !isconvex(now,in))return;
  //if (!isok(now,in))return;

  if (dist(in[now-1])-total > eps){
    return; 
  }
  
  if (abs(dist(in[now-1])-total)<eps){
    int tmp = PolygonArea(in,now);
    if (isok(n,in))ans=max(ans,tmp);//,cout << tmp<<endl;
    return;    
  }
  

  if (n == pnow+1){
    int index=len[pnow];
    if (abs(dist(in[now-1])-index)<eps ){
      int tmp = PolygonArea(in,now);
      if (isok(n,in))ans=max(ans,tmp);//,cout << tmp<<endl;
    }
    return;
  }

  rep(i,cand[pnow].size()){
    in[now].real()=in[now-1].real()+cand[pnow][i].real();
    in[now].imag()=in[now-1].imag()+cand[pnow][i].imag();
    
    if (in[now].imag()<0 && in[now].real()>=0)continue;
    search(n,now+1,pnow+1,len,in,cand,total-len[pnow]);
  }
}

main(){
  int n;
  while(cin>>n && n){
    int r[n];
    int num[100];
    int len[100];
    int p=0;
    double total=0;
    P in[10];
    vector<P> cand[10];
    
    ans = -1;
    
    rep(i,n){
      cin>>r[i];
      total+=r[i];
    }
    sort(r,r+n);
    in[0]=ori;


    rep(i,n){
      cand[i].clear();
      cand[i]=make_cand(r[i]);
    }

    /*    
    int findmax=0;
    rep(i,n){
      if (cand[i].size() > cand[findmax].size())findmax=i;
      else if (cand[i].size()==cand[findmax].size() &&
	       r[i] > r[findmax])findmax=i;
    }
    
    swap(r[0],r[findmax]);
    sort(r+1,r+n);
    */    
    
    rep(i,cand[0].size()){
      if (cand[0][i].imag() >=0 && cand[0][i].real()>0);
      else {
	cand[0].erase(cand[0].begin()+i);
	i--;
      }
    }

    set<vector<int> >S;
    do{
      
      vector<int> tmp;
      REP(i,1,n){
	tmp.pb(r[i]);
      }
      if (S.find(tmp) != S.end()){
	continue;
      }      
      reverse(ALL(tmp));
      S.insert(tmp);
      

      REP(i,1,n){
	cand[i].clear();
	cand[i]=make_cand(r[i]);
      }
      //      rep(i,n)cout << r[i] <<" ";cout << endl;
      search(n,1,0,r,in,cand,total);
    }while(next_permutation(r+1,r+n));

    cout << ans << endl;
  }
}
accuracy: 1
time_ms: 5550
memory_kb: 924
final_rank: 1