wlgur1023   2년 전

재귀 함수를 구현할 때

int func(int currentValue){

    int ret = func(currentValue + newValue);

    return ret;

}

처럼 인자에 넘겨지는 값을 미리 더한 후 재귀함수로 넘기는 것과

int func(){

    int ret = currentValue + func();

    return ret;

}

처럼 재귀함수에서 리턴된 값을 받고 나서 현재 값을 더하는 것에 차이가 있나요?

// 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;
}

아래 코드는 두 번째 방식으로 짠 것인데 통과하였습니다.

더하고서 재귀 함수로 넘기나, 리턴 받고 마지막에 더하나 결국 마지막에 다 더해진 값은 같아야 하지 않나요?

첫 번째 코드와 두 번째 코드 모두 게시판에 적힌 반례는 전부 통과하였습니다.

도대체 두 방식의 차이가 무엇일까요?

감사합니다.

ssonoo   2년 전

혹시 차이를 알아 내셨을 까요? 저도 같은 이유로 계속 틀렸었는데 도저히 모르겠습니다...

mmj9808   2년 전

저도 똑같이 틀렸는데 왜 그런지 잘 모르겟네요

댓글을 작성하려면 로그인해야 합니다.