// Wrong Answer
#include <iostream>
#include <vector>
using namespace std;
#define MAX 16
#define INF 1000000000
int n; // 도시의 수
double dist[MAX+1][MAX+1]; // 두 도시 간의 거리를 저장하는 배열
int dp[MAX+1][1 << 16]; // [현재 도시][방문 상태]
inline unsigned int arrayToBitmask(vector <bool> &v){
unsigned int ret = 0;
for(int i = 0; i < v.size(); i++)
if(v[i]) ret |= (1 << i);
return ret;
}
// path : 지금까지 만든 경로
// visited : 각 도시의 방문 여부
// currentLength : 지금까지 만든 경로의 길이
// 나머지 도시들을 모두 방문하는 경로들 중 가장 짧은 것의 길이를 반환한다.
int shortestPath(vector<int> &path, vector<bool> &visited, int currentLength){
int& ret = dp[path.back()][arrayToBitmask(visited)];
// 기저 사례 : 모든 도시를 다 방문했을 때는 시작 도시로 돌아가고 종료한다.
if(path.size() == n){
// cout << path[0] << endl;
if(dist[path.back()][path[0]])
return currentLength + dist[path.back()][path[0]];
else
return INF;
}
if(ret) return ret;
ret = INF; // 매우 큰 값으로 초기화
// 다음 방문할 도시를 전부 시도해 본다.
for(int next = 0; next < n; ++next){
if(visited[next]) continue;
int here = path.back();
if((dist[here][next] == 0)) continue;
path.push_back(next);
visited[next] = true;
// 나머지 경로를 재귀 호출을 통해 완성하고 가장 짧은 경로의 길이을 얻는다.
int cand = shortestPath(path, visited, currentLength + dist[here][next]);
ret = min(ret, cand);
visited[next] = false;
path.pop_back();
}
return ret;
}
void Input(){
cin >> n;
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
cin >> dist[i][j];
}
int main(){
vector<int> path = { 0 };
vector<bool> visited(MAX, 0);
visited[0] = true;
Input();
cout << shortestPath(path, visited, 0);
return 0;
}
위의 코드는 첫 번째 방식으로 짠 것인데 6%에서 틀리고
// AC
#include <iostream>
#include <vector>
using namespace std;
#define MAX 16
#define INF 1000000000
int n; // 도시의 수
double dist[MAX][MAX]; // 두 도시 간의 거리를 저장하는 배열
int dp[MAX+1][1 << 16]; // [현재 도시][방문 상태]
inline unsigned int arrayToBitmask(vector <bool> &v){
unsigned int ret = 0;
for(int i = 0; i < v.size(); i++)
if(v[i]) ret |= (1 << i);
return ret;
}
// path : 지금까지 만든 경로
// visited : 각 도시의 방문 여부
// 나머지 도시들을 모두 방문하는 경로들 중 가장 짧은 것의 길이를 반환한다.
int shortestPath(vector<int> &path, vector<bool> &visited){
int& ret = dp[path.back()][arrayToBitmask(visited)];
// 기저 사례 : 모든 도시를 다 방문했을 때는 시작 도시로 돌아가고 종료한다.
if(path.size() == n){
// cout << path[0] << endl;
if(dist[path.back()][path[0]])
return dist[path.back()][path[0]];
else
return INF;
}
if(ret) return ret;
ret = INF; // 매우 큰 값으로 초기화
// 다음 방문할 도시를 전부 시도해 본다.
for(int next = 0; next < n; ++next){
if(visited[next]) continue;
int here = path.back();
if((dist[here][next] == 0)) continue;
path.push_back(next);
visited[next] = true;
// 나머지 경로를 재귀 호출을 통해 완성하고 가장 짧은 경로의 길이을 얻는다.
int cand = shortestPath(path, visited) + dist[here][next];
ret = min(ret, cand);
visited[next] = false;
path.pop_back();
}
return ret;
}
void Input(){
cin >> n;
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
cin >> dist[i][j];
}
int main(){
vector<int> path = { 0 };
vector<bool> visited(MAX, 0);
visited[0] = true;
Input();
cout << shortestPath(path, visited);
return 0;
}
아래 코드는 두 번째 방식으로 짠 것인데 통과하였습니다.
더하고서 재귀 함수로 넘기나, 리턴 받고 마지막에 더하나 결국 마지막에 다 더해진 값은 같아야 하지 않나요?
wlgur1023 2년 전 1
재귀 함수를 구현할 때
int func(int currentValue){
int ret = func(currentValue + newValue);
return ret;
}
처럼 인자에 넘겨지는 값을 미리 더한 후 재귀함수로 넘기는 것과
int func(){
int ret = currentValue + func();
return ret;
}
처럼 재귀함수에서 리턴된 값을 받고 나서 현재 값을 더하는 것에 차이가 있나요?
위의 코드는 첫 번째 방식으로 짠 것인데 6%에서 틀리고
아래 코드는 두 번째 방식으로 짠 것인데 통과하였습니다.
더하고서 재귀 함수로 넘기나, 리턴 받고 마지막에 더하나 결국 마지막에 다 더해진 값은 같아야 하지 않나요?
첫 번째 코드와 두 번째 코드 모두 게시판에 적힌 반례는 전부 통과하였습니다.
도대체 두 방식의 차이가 무엇일까요?
감사합니다.