무식한 방법으로 생각하면 개에서 개의 점의 순서쌍을 개 고르는 경우를 모두 시도하고 순서쌍의 순서를 바꾼 경우도 모두 고려해주면 바로 시간초과가 납니다. 벡터의 성질을 이용해서 더 간단하게 바꿀 수 없을까요?
개의 점으로 이루어진 집합 에서 임의의 서로 다른 두 점 를 골랐을 때 와 를 끝점으로 하는 벡터는 개가 있고, 각각 좌표 성분으로 표기하면 혹은 가 됩니다
최적해를 찾기 위해 우리가 고른 개의 벡터의 집합을 라고 해보겠습니다. 벡터의 합은 결국 또 다른 새로운 벡터인데 이는 각각의 좌표 성분을 모두 더하면 만들어 낼 수 있습니다. 그 벡터를 라고 해 보겠습니다
가 됩니다.
의 원소 벡터들은 혹은 로 생각할 수 있는데, 연산의 순서가 상관이 없으므로 우리는 개의 정점에서 개의 음수를 고르면 를 만들어 낼 수 있다는 것을 알 수 있습니다.
또한 벡터의 성분이 주어져 있을 때 그 벡터의 크기는 으로 쉽게 구할 수 있습니다.
public class Main {
static int N;
static Pair[] P;
static boolean[] picked;
// P[here, N)에서 k개를 고르는 경우 모두 시도
static double bfc(int here, int k) {
if (here == N) {
if (k != 0) return Double.MAX_VALUE;
long y = 0; long x = 0;
for (int i = 0; i < N; i++) {
if (picked[i]) { y -= P[i].y; x -= P[i].x; }
else { y += P[i].y; x += P[i].x; }
}
return Math.sqrt(y * y + x * x);
}
double min = bfc(here + 1, k);
picked[here] = true;
min = Math.min(min, bfc(here + 1, k - 1));
picked[here] = false;
return min;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder bw = new StringBuilder();
int TC = Integer.parseInt(br.readLine());
while (TC-- > 0) {
N = Integer.parseInt(br.readLine());
P = new Pair[N];
picked = new boolean[N];
for (int i = 0; i < N; i++) {
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
P[i] = new Pair(Integer.parseInt(st.nextToken()), Integer.parseInt(st.nextToken()));
}
bw.append(bfc(0, N / 2)).append("\n");
}
System.out.print(bw);
}
}
class Pair {
int y, x;
Pair(int y, int x) { this.y = y; this.x = x; }
}
개에서 개를 선택하는 경우를 모두 시도하므로 이 경우의 수는 최대 이 됩니다. 하지만 모든 원소마다 선택 하느나 하지 않느냐로 재귀 호출하므로 이 됩니다.