리뷰
https://www.acmicpc.net/problem/16991
간선의 가중치가 실수인 외판원 순회 문제
전역 변수
- N : 배열의 최대 크기를 정의할 상수 변수
- n : 도시의 수를 저장할 변수
- Pos : 도시의 좌표를 정의할 구조체
- poses : 도시의 좌표들을 저장할 Pos타입 배열
- Edge : 간선 정보를 정의할 구조체
- edges : 간선 정보를 저장할 Edge타입 벡터 배열
- dp : 방문 최소값을 저장할 2차원 배열
함수
1. get_dist
double get_dist(const Pos& pos1, const Pos& pos2) {
return sqrt(pow(pos1.x - pos2.x, 2) + pow(pos1.y - pos2.y, 2));
}
두 좌표 사이의 거리를 구하기 위한 함수
2. dfs
double dfs(int mask, int cur) {
if (mask == (1 << n) - 1) {
double dist_to_start = get_dist(poses[cur], poses[1]);
return dp[mask][cur] = dist_to_start;
}
if (dp[mask][cur] != -1.0f) return dp[mask][cur];
dp[mask][cur] = 2e9;
for (const Edge& edge : edges[cur]) {
if (!(mask & (1 << edge.nn - 1))) {
dp[mask][cur] = min(dp[mask][cur], dfs(mask | (1 << edge.nn - 1), edge.nn) + edge.nv);
}
}
return dp[mask][cur];
}
모든 도시를 방문하고 원점으로 돌아오는 최소값을 구하기 위한 함수
문제풀이
- n값을 입력 받고, n개의 도시 좌표 정보를 입력 받아 poses배열에 저장해 준다.
- 각 도시간 거리 정보를 get_dist함수로 구한 뒤 인접 리스트 edges에 양방향 간선으로 저장해 준다.
- dp배열의 초기값을 -1.0으로 저장하여 미방문을 표시해 준다.
- dfs함수에 1번 노드부터 방문함을 표시해 주고 재귀를 통해 dp배열을 최신화 해준다.
- dfs함수는 현재까지의 방문 정보 mask와 현재 위치한 도시의 번호 cur을 매개변수로 전달 받는다.
- 첫번째 기저 조건으로 모든 도시에 방문한 경우 현재 도시에서 1번 도시까지의 거리를 get_dist로 구한 뒤 dp값을 갱신 후 해당 값을 리턴해 준다.
- 두번째 기저 조건으로 이미 최적값이 채워진 도시인 경우 그 값을 바로 리턴해 준다.
- 두 기저 조건에 해당하지 않는 경우 dp[mask][cur]을 매우 큰 값으로 초기화 해준다.
- cur의 인접 리스트를 순회하며 아직 방문하지 않은 경우 방문을 진행해 준다.
- dp[mask][cur]을 dfs함수를 통해 다음 도시를 방문한 리턴 값 + 간선의 가중치 중 작은 값으로 갱신해 준다.
- 모든 인접 리스트를 순회한 뒤 dp[mask][cur]에 저장된 값을 리턴해 준다.
- dfs함수의 최종 리턴값을 출력해 준다.
트러블 슈팅
- dfs함수를 그대로 호출해 주니 소숫점 아랫자리가 뒤죽 박죽이였다. 이대로 제출하니 Fail을 받았다.
- cout << fixed, cout.precision을 통해 소숫점 아래 자리를 고정으로 출력해 주어 AC를 받았다.
참고 사항
- 도시의 번호가 1-based-indexing이므로 방문 여부나 매개변수 전달 시 0-based-indexing처리 후 전달해 주었다.
- fixed, precision을 통해 소숫점 자리를 고정으로 출력해 주었다.
정답 코드
#include<iostream>
#include<vector>
#include<cmath>
using namespace std;
const int N = 17;
int n;
struct Pos {
int x, y;
};
Pos poses[N];
struct Edge {
int nn;
double nv;
};
vector<Edge> edges[N];
double dp[1 << N][N];
double get_dist(const Pos& pos1, const Pos& pos2) {
return sqrt(pow(pos1.x - pos2.x, 2) + pow(pos1.y - pos2.y, 2));
}
double dfs(int mask, int cur) {
if (mask == (1 << n) - 1) {
double dist_to_start = get_dist(poses[cur], poses[1]);
return dp[mask][cur] = dist_to_start;
}
if (dp[mask][cur] != -1.0f) return dp[mask][cur];
dp[mask][cur] = 2e9;
for (const Edge& edge : edges[cur]) {
if (!(mask & (1 << edge.nn - 1))) {
dp[mask][cur] = min(dp[mask][cur], dfs(mask | (1 << edge.nn - 1), edge.nn) + edge.nv);
}
}
return dp[mask][cur];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
int x, y; cin >> x >> y;
poses[i] = { x, y };
}
for (int i = 1; i < n; ++i) {
for (int j = i + 1; j <= n; ++j) {
const Pos& pos1 = poses[i];
const Pos& pos2 = poses[j];
double dist = get_dist(pos1, pos2);
edges[i].push_back({ j, dist });
edges[j].push_back({ i, dist });
}
}
for (int i = 0; i < (1 << N); i++) {
for (int j = 0; j < N; j++) {
dp[i][j] = -1.0f;
}
}
cout << fixed;
cout.precision(10);
cout << dfs(1, 1);
}
728x90
'알고리즘 공부 > C++' 카테고리의 다른 글
[P5] 백준 12930번 두 가중치 C++ 다익스트라, 다이나믹 프로그래밍 (0) | 2025.06.18 |
---|---|
[G3] 백준 13418번 학교 탐방하기 C++ 최소 스패닝 트리(MST), 정렬 (0) | 2025.06.17 |
[G2] 백준 2632번 피자판매 C++ 누적합, 해시맵 (0) | 2025.06.15 |
[G3] 백준 22860번 폴더 정리 (small) C++ 해시맵, 해시셋, 깊이 우선 탐색, 트리 (2) | 2025.06.14 |
[G5] 백준 3649번 로봇 프로젝트 C++ 투 포인터, 정렬 (0) | 2025.06.13 |