레이블이 재귀인 게시물을 표시합니다. 모든 게시물 표시
레이블이 재귀인 게시물을 표시합니다. 모든 게시물 표시

2017년 12월 2일 토요일

1135 뉴스 전하기

1135 뉴스 전하기 https://www.acmicpc.net/problem/1135

민식이는 트리의 루트이다.
그의 직속 부하에게 한번에 한사람씩 전화를 하는데 1분이 소모된다.
모든 사람들이 전화를 받기까지 걸리는 최소시간을 구하는 문제다.

처음에는 문제를 잘못이해해서 모든 전화의 시작은 민식으로부터 시작되는줄 알았다.

두번 째는 현재위치에서 갈 수 있는 가장 depth가 깊은곳을 먼저 가는것으로 해주었다.
이것은 최적값이 아니다.

세번째는 현재위치에서 가장 많은 사람이 있는 곳으로 먼저 가도록 해주었다.
이것 또한 최적값이 아니다.

정확한 풀이는 다음과 같다.
현재위치에서 갈 수 있는 다음위치의 subtree의 모든 사람들이 전화를 받는 
최소시간이 가장 큰곳으로 먼저 가야한다.
#include <cstdio>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
int N;
vector<int> adj[51];
priority_queue<pair<intint>> pq[51];
int dfs(int here) {
    int ret = 0;
    int plus = 0;
    for (int &next : adj[here]) {
        pq[here].push({ 1 + dfs(next), next });
    }
    while (!pq[here].empty()) {
        int cost = pq[here].top().first;
        int next = pq[here].top().second;
        pq[here].pop();
        ret = max(ret, cost + plus++);
    }
    return ret;
}
int main() {
    scanf("%d"&N);
    for (int n = 0;n < N;n++) {
        int u;
        scanf("%d"&u);
        if (!n) continue;
        adj[u].push_back(n);
    }
    printf("%d\n", dfs(0));
    return 0;
}
cs

Top-Down 방식으로 풀리는 좋은 문제였다.

2017년 3월 31일 금요일

1588 수열

백준 1588 수열 https://www.acmicpc.net/problem/1588

문제는 간단하다. 매초마다 1은 132로 2는 211로 3은 232로 바뀐다. N초후에 왼쪽L번째부터 R번째까지 1,2,3숫자의 개수를 구하는 문제이다.

이 문제는  보고 어떻게 풀지 생각은 났는데 구현이 잘 안됬던 문제이다. 막연하게 연산시간을 생각하지 않고 짜면 금방 풀 수 있지만 당연히 TLE가 나올것이다. 그래서 시간을 줄이는 방법 두가지를 사용하였다.




<1>. L ~ R 이외의 범위 탐색X
1부터 시작한다면 위의 그림처럼 $N$초뒤에는 ${3}^{N}$의 숫자가 존재할 것이다. 이것은 어떤 시작점(1 또는 2또는 3)으로부터 $N$초후에 생기게 될 범위를 알 수 있다는 말이다.

예를들어 1에서 시작하여 2초뒤에 범위는 0~8(배열 0시작 기준)이다. 또 1초때 2번 index에서 시작하여 1초후의 범위는 6~8이다.
범위를 알게되면 굳이 탐색할 필요도 없는 공간에 대해 시간투자를 안해도 된다. 


<2>. L ~ R 내부 범위 탐색X
사실 $N$초후의 시작부터 마지막까지 1, 2, 3의 개수는 for문 N번만 돌리면 알 수 있다.
$a,b,c$를 각각 1,2,3의 개수라고 한다면 다음과 같이 나타낼 수 있다.
$a = a+2b$
$b = a+b+2c$  
$c = a+c$

따라서 시작점으로부터 N초후의 범위가 완전히 L ~ R사이에 존재한다면 이것역시 탐색을 할 필요가 없게된다.
예를들어 $시작=1, L=3, R=6, N =2$이라고 하면 
0초때는 0~8까지의 범위를 가질 것이다. 1초뒤에는 1, 3, 2의 숫자가 각각 0~2, 3~5, 6~8의 범위를 가지는데 온전한 범위 이내에 있는 3~5, 6~8은 탐색할 필요가 없다는것이다.

사실상 위의 예는 1초때 0~2의 범위도 범위 밖이므로 탐색할 필요가 없기때문에 1초까지만 탐색을 하면된다. (<1>의 경우)


#include <cstdio>
int X, L, R, N;
long long an1 = 0, an2 = 0, an3 = 0;
long long dp[4][21][4];
long long pow(int x, int y){
    if (y == 0)
        return 1;
    else if (y == 1)
        return x;
    if (y & 1)
        return pow(x, y - 1)*pow(x, 1);
    else
        return pow(x, y / 2)*pow(x, y / 2);
}
int sqrt(long long x){
    int cnt = 0;
    for (x; x != 1; x /= 3, cnt++);
    return cnt;
}
void cul(){
    long long a = 1, b = 0, c = 0;
    long long na, nb, nc;
    for (int n = 1; n <= 20; n++){
        na = a + 2 * b;
        nb = a + b + 2 * c;
        nc = a + c;
        a = na; b = nb; c = nc;
        dp[1][n][1= a; dp[1][n][2= b; dp[1][n][3= c;
    }
    a = 0; b = 1; c = 0;
    for (int n = 1; n <= 20; n++){
        na = a + 2 * b;
        nb = a + b + 2 * c;
        nc = a + c;
        a = na; b = nb; c = nc;
        dp[2][n][1= a; dp[2][n][2= b; dp[2][n][3= c;
    }
    a = 0; b = 0; c = 1;
    for (int n = 1; n <= 20; n++){
        na = a + 2 * b;
        nb = a + b + 2 * c;
        nc = a + c;
        a = na; b = nb; c = nc;
        dp[3][n][1= a; dp[3][n][2= b; dp[3][n][3= c;
    }
}
void loop(int num, long long range, long long first, long long second){
    if ((range != 1&& (R < first || (L > second)))
        return;
    if ((range != 1&& (L <= first && second <= R)){
        int r = sqrt(range);
        an1 += dp[num][r][1]; an2 += dp[num][r][2]; an3 += dp[num][r][3];
        return;
    }
    if (range == 1){
        if (L <= first && R >= first){
            if (num == 1)
                an1++;
            else if (num == 2)
                an2++;
            else
                an3++;
            return;
        }
        else
            return;
    }
    long long new_range = range / 3;
    long long f1 = first, s1 = f1 + new_range - 1;
    long long f2 = s1 + 1, s2 = f2 + new_range - 1;
    long long f3 = s2 + 1, s3 = f3 + new_range - 1;
    if (num == 1){
            loop(1, new_range, f1, s1);
            loop(3, new_range, f2, s2);
            loop(2, new_range, f3, s3);
    }
    else if (num == 2){
            loop(2, new_range, f1, s1);
            loop(1, new_range, f2, s2);
            loop(1, new_range, f3, s3);
    }
    else if (num == 3){
            loop(2, new_range, f1, s1);
            loop(3, new_range, f2, s2);
            loop(2, new_range, f3, s3);
    }
    return;
}
int main(){
    scanf("%d%d%d%d"&X, &L, &R, &N);
    cul();
    loop(X, pow(3, N), 0, pow(3, N) - 1);
    printf("%d %d %d\n", an1, an2, an3);
    return 0;
}
cs