알고리즘 트레이닝 - 스티븐 할림/펠릭스 할림의 첫 문제입니다.
각자의 수준을 알아보라는 목적으로 낸 문제인데, 생각보다 수월하게 풀려서 확실히 10년대 초반에 비해 실력의 하한이 올라갔다는 생각이 듭니다. 그런 점에서 할림 형제는 목적을 이룬 것 같네요 ㅋㅋ
문제 본문
요약: 주어진 개의 점들을 개의 그룹으로 나눕니다. 이 때, 개의 각 그룹에서 두 점 사이의 거리가 나올텐데, 그 거리의 총합이 최소가 되게 하고 싶습니다. 이 때, 총합을 구해주세요.
수식이 오히려 불편하기 때문에 직접 연산량을 구하도록 하겠습니다.
라고 합시다.
naive하게 번의 연산을 하면 TLE가 명백합니다.
그러면, 중복을 해결했을때 어떻게 될까요?
가 됩니다. 이므로 충분히 돌아갈 것입니다.
뒤이어 나올 두 가지 솔루션은 모두 중복을 제거하는 풀이이기 때문에 확신을 갖고 임하면 됩니다.
상세한 시간 복잡도 분석은 뒤에서 하겠습니다.
/*
Code by hgmhc.
* 20분 컷.
* 1 WA 후 AC. 변명을 하자면 책에 요약된 문제 내용이 실제 description과 달라서 다른 형태로 제출함.
* 28번째 줄을 쓰지 않은걸 디버깅 함.
*/
#include <bits/stdc++.h>
#define REP(i,a,b) for (auto i = (a); i <= (b); ++i)
#define debug(x) cout << #x << " is " << x << el
#define el '\n'
using namespace std; using ll = long long;
const int N = 17;
const double INF = 1e9;
int n;
complex<double> point[N];
bool checked[N];
double f(int k = 1) {
if (k == n+1) return 0;
int x = -1;
REP(i,1,2*n) if (not checked[i]) {x = i; break;}
double best = INF;
checked[x] = true;
REP(y,x+1,2*n) {
if (checked[y]) continue;
checked[y] = true;
best = min(best,abs(point[x]-point[y])+f(k+1));
checked[y] = false;
}
checked[x] = false;
return best;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
int tc = 0;
cout << fixed;
cout.precision(2);
while (++tc) {
cin >> n;
if (n == 0) return 0;
REP(i,1,2*n) {
string s; cin >> s;
double x, y; cin >> x >> y;
point[i] = {x,y};
}
cout << "Case " << tc << ": " << f() << el;
}
}
이 제 코드 속 알고리즘 수행 시간입니다.
따라서 의 시간 복잡도가 통과한다는 것을 확인하면 충분하다.
믿음의 치환법으로, 라 하면, 옳지 않다.
라 하면,
이므로 만족한다.
이므로 1초에는 안 돌아가되, 기도했을 시 제한 시간 3초 내에 돌아갈 수 있습니다. 또한, 이 naive 버전이란 것을 감안하면 좀 더 빠를 것입니다.
사실 저도 이렇게까지는 안 재보고 넣었는데 실전이였으면 큰일 날 번 했군요...
역시 백트래킹 시간 복잡도 분석은 어렵습니다...
오늘 마스터 정리와 치환법 알아가는 것은 상당히 기분이 좋군요...
이므로 통과합니다.
#include <bits/stdc++.h>
#define REP(i,a,b) for (auto i = (a); i <= (b); ++i)
#define debug(x) cout << #x << " is " << x << el
#define el '\n'
using namespace std; using ll = long long;
const int N = 16;
const double INF = 1e9;
int n;
complex<double> point[N];
double dp[1<<N];
double solve() {
fill_n(dp,1<<(2*n),INF);
dp[0] = 0;
REP(mask,0,1<<(2*n)) {
REP(i,0,2*n-1) if (mask&(1<<i)) {
REP(j,i+1,2*n-1) if (mask&(1<<j)) {
dp[mask] = min(dp[mask],dp[mask^(1<<i)^(1<<j)]+abs(point[i]-point[j]));
}
}
}
return dp[(1<<(2*n))-1];
}
int main() {
cin.tie(0)->sync_with_stdio(0);
int tc;
cout << fixed;
cout.precision(2);
while (++tc) {
cin >> n;
if (n == 0) return 0;
REP(i,0,2*n-1) {
string s; cin >> s;
double x, y; cin >> x >> y;
point[i] = {x,y};
}
cout << "Case " << tc << ": " << solve() << el;
}
}