와.. 쉬울 줄 알고 고른 문제였는데 꽤나 골치 아팠다..
왜 때문인지 조합을 떠올리는 거 자체가 어려웠다..
생각해보니 순열 조합 활용하는 문제 거의 안 풀어본 것 같다
처음에는 BFS 활용해서 집마다 가장 가까운 치킨 집을 표시하고..
뭐 이런식으로 구현하듯이 풀려고 했는데
뭔가 아닌 것 같은 느낌이 들어서 찾아보니 시간초과를 내는 코드임을 알 수 있었다 환장 🤷♀️
도시에 있는 치킨집 중에서 최대 M개를 고르고, 나머지 치킨집은 모두 폐업시켜야 한다. 어떻게 고르면, 도시의 치킨 거리가 가장 작게 될지 구하는 프로그램을 작성하시오.
여기서 조합을 써야겠다는 느낌이 빡- 왔다면 코드 짜는 거 자체는 어렵지 않다
나는 갠적으로 조합 떠올리기까지 시간이 오래걸려서 힘들었다..
조합을 떠올린 후에는 아래와 같은 순서로 답을 구하면 된다
조합 제대로 익히고 넘어가자! 라는 생각으로
① DFS ② next_permutation 활용하는 두 가지 방법으로 구현해보았다
DFS는
중복이 없도록 탐색 시작 지점?을 알려주는 index와
선택한 개수를 알려주는 count를 활용하며
재귀 호출 전/후에 visited 값을 true/false로 바꿔주며 백트래킹이 가능하도록 한다
C++ 같은 경우는 algorithm 라이브러리에 있는 next_permutation 함수를 활용하면
보다 쉽게 조합을 구할 수 있다
다만 순열과 달리 조합은 순서가 상관없이 원소를 뽑는 것이기 때문에
cf) {1,2,3,4,5} 중에서 {1,2,3} 과 {3,2,1} 은 동일한 조합으로 여겨짐
선택하려는 숫자들이 들어있는 원본 벡터가 아니라
새로운 벡터에 N개의 원소 중 선택하려는 원소 개수 k만큼 1을, N-k만큼 0을 push하고
next_permutaion을 돌린 후 값이 1인 인덱스에 해당하는 값들만 원본 벡터에서 선택한다
// Solution #1) dfs 활용
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
#define MAX_INT 2147483647
struct House {
int r, c;
};
struct Chicken {
int r, c;
bool visited;
};
int N, M;
int minCityDistance = MAX_INT; // 도시의 치킨 거리의 최솟값
vector<House> house;
vector<Chicken> chicken;
void calculateDistance() {
// 각 집에 대해 선택된 M개의 치킨집과의 거리 중 최솟값 구하기
// 이를 다 더하여 도시의 치킨 거리 구하고 현재의 최솟값 보다 작을 경우 최솟값 업데이트
int cityDistance = 0;
for (int i=0; i<house.size(); i++) {
int minHouseDistance = MAX_INT;
for (int j=0; j<chicken.size(); j++) {
if (chicken[j].visited) {
int houseDistance = abs(house[i].r - chicken[j].r) + abs(house[i].c - chicken[j].c);
if (houseDistance < minHouseDistance) {
minHouseDistance = houseDistance;
}
}
}
cityDistance += minHouseDistance;
}
if (cityDistance < minCityDistance) {
minCityDistance = cityDistance;
}
}
void selectChicken(int index, int count) {
// dfs + 조합
// 폐업시키지 않을 치킨집 최대 M개 고르기
if (count == M) {
calculateDistance();
}
for (int i=index; i<chicken.size(); i++) {
if (!chicken[i].visited) {
chicken[i].visited = true;
selectChicken(i, count + 1);
chicken[i].visited = false;
}
}
}
int main() {
scanf("%d %d", &N, &M);
int flag;
for (int i=0; i<N; i++) {
for (int j=0; j<N; j++) {
scanf("%d", &flag);
if (flag == 1) { house.push_back({i, j}); }
else if (flag == 2) { chicken.push_back({i, j, false}); }
}
}
selectChicken(0, 0);
printf("%d", minCityDistance);
return 0;
}
// Solution #2) next_permutation 활용
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
#define MAX_INT 2147483647
int N, M;
int minCityDistance = MAX_INT;
vector<pair<int, int>> house;
vector<pair<int, int>> chicken;
vector<int> selected;
void calculateDistance() {
int cityDistance = 0;
for (int i=0; i<house.size(); i++) {
int minHouseDistance = MAX_INT;
for (int j=0; j<chicken.size(); j++) {
if (selected[j] == 1) {
int houseDistance = abs(house[i].first - chicken[j].first) + abs(house[i].second - chicken[j].second);
if (houseDistance < minHouseDistance) {
minHouseDistance = houseDistance;
}
}
}
cityDistance += minHouseDistance;
}
if (cityDistance < minCityDistance) {
minCityDistance = cityDistance;
}
}
void selectChicken() {
for (int i=0; i<chicken.size()-M; i++) { selected.push_back(0); }
for (int i=0; i<M; i++) { selected.push_back(1); }
do {
calculateDistance();
} while(next_permutation(selected.begin(), selected.end()));
}
int main() {
scanf("%d %d", &N, &M);
int flag;
for (int i=0; i<N; i++) {
for (int j=0; j<N; j++) {
scanf("%d", &flag);
if (flag == 1) { house.push_back({i, j}); }
else if (flag == 2) { chicken.push_back({i, j}); }
}
}
selectChicken();
printf("%d", minCityDistance);
return 0;
}
#include <limits>
를 해주면INT_MAX
값을 가져다 쓸 수 있다!
#define INT_MAX 2147483647
과 같이 define 해주지 않아도! 꿀팁 😊
(백준에서는 #include <limits>
는 인식이 안돼서 #include <limits.h>
해주어야 한다고 함)
cf) C 및 C++ 정수 제한
https://docs.microsoft.com/ko-kr/cpp/c-language/cpp-integer-limits?view=msvc-170