8.16 문제: 두니발 박사의 탈옥 ( 문제ID : NUMB3RS, 난이도: 중)

역시 완전탐색에서 시작하자

감옥이 있는 마을 p에서 시작해서 마을 q까지 가는 길이 n+1인 경로들을 모두 생성하는 작업은 완전 탐색의 전매특허입니다.

  • p에서 시작해 n번 인접합 마을로 옮기는 모든 경로를 생성하고, 이 중 q에서 끝나는 것들이 출현할 확률을 계산해 그들의 합을 반환합니다.
int n, d, p , q;
// connected[i][j]= 마을 i,j가 연결되어 있나 여부
// deg[i] = 마을 i와 연결된 마을의 개수
int connected[51][51], deg[51];
double search(vector<int>& path){
    // 기저 사례: d일이 지난 경우
    if(path.size() == d+1){
        // 이 시나리오가 q에서 끝나지 않는다면 무효
        if(path.back() != q)return 0.0;
        // path의 출현 확률을 계산한다
        double ret = 1.0;
        for(int i = 0; i + 1 < path.size(); ++i)
            ret /= deg[path[i]];
        return ret;
    }
    double ret = 0;
    // 경로의 다음 위치를 결정한다.
    for(int there = 0; there < n; there++)
        if(connected[path.back()][there]){
            path.push_back(there);
            ret += search(path);
            path.pop_back();
        }
    return ret;
}

메모이제이션

search()에 전달되는 path 변수의 용도 3가지

  • path의 마지막 원소는 현재 위치: 다음 마을을 결정할 때 필요합니다.
  • path의 길이는 탈옥 후 지난 날짜: 경로가 끝났는지 알 때 필요합니다.
  • 확률계산: 완성된 경로의 출현 확률을 계산할 때 필요합니다.

우리는 나머지 조각들을 해결하는 데 필요한 최소의 정보만 남기고 나머지를 없애는 연습을 해본적이 있습니다. 다음과 같은 변화를 적용하면 됩니다.

  • path 대신 현재 위치 here와 탈옥 후 지난 날짜 days를 재귀 호출에 전달합니다.
  • 전체 경로의 확률을 계산하는 것이 아니라, 현재 위치에서 시작해서 남은 날짜 동안 움직여 q에 도달할 확률을 계산합니다.
search2(here, days) = 두니발 박사가 days일째에 here 마을에 숨어 있을 , 마지막 날에 q 마을에
                      있을 조건부 확률을 반환한다.
int n, d, p , q;
// cache[][]는 -1로 초기화해 둔다.
double cache[51][101];
// connected[i][j]= 마을 i,j가 연결되어 있나 여부
// deg[i] = 마을 i와 연결된 마을의 개수
int connected[51][51], deg[51];
// days일째에 here번 마을에 숨어 있다고 가정하고,
// 마지막 날에 q번 마을에 숨어 있을 조건부 확률을 반환한다.
double search2(int here, int days){
    // 기저 사례: d일이 지난 경우
    if(days == d)return (here == q ? 1.0 : 0.0);
    // 메모이제이션
    double& ret = cache[here][days];
    if(ret > -0.5)return ret;
    ret = 0.0;
    for(int there = 0; there < n; there++)
        if(connected[here][there])
            ret += search2(there, days+1) / deg[here];
    return ret;
}

반대방향에서 풀기

double search3(int here, int days){
    // 기저 사례: 0일째
    if(days == 0) return (here == p ? 1.0 : 0.0);
    // 메모이제이션
    double& ret = cache[here][days];
    if(ret > -0.5)return ret;
    ret = 0.0;
    for(int there=0; there < n; there++)
        if(connected[here][there])
            ret += search3(there, days-1) / deg[there];
    return ret;
}