[Algorightm] PCCP 기출문제 모음

SnowCat·2023년 11월 28일
0

CS - Algorithm

목록 보기
5/5
post-thumbnail

1번

문제 링크

  • 문제의 입력이 충분히 짧기 때문에 직관적으로 있는 그대로 풀어도 상관이 없다.
  • 조건에 맞추어 문제에 맞게 코드를 구현하면 된다.
function solution(bandage, health, attacks) {
    let [t, x, y] = bandage; //시전 시간, 초당 회복량, 추가 회복량
    let currentTime = 0; // 현재시간
    let currentHealth = health; // 현재체력
    let maxHealth = health; //최대채력
    let continuousHealTime = 0; // 연속 힐 시간
    
    // 캐릭이 죽거나 공격이 끝나면 계산 종료
    while (attacks.length && currentHealth > 0) {
        const [attackTime, attackDamage] = attacks[0]; //공격 시간, 피해량
        
        //공격이 없어 힐을 하는 경우
        if (currentTime < attackTime) {
            currentHealth += x;
            continuousHealTime++;
            
            // 추가 회복량을 받는 경우
            if(continuousHealTime >= t) {
                currentHealth += y;
                continuousHealTime = 0;
            }
            
            // 최대 채력을 초과할 수 없음
            if (currentHealth > maxHealth) currentHealth = maxHealth;
        }
        
        //공격이 있어 데미지를 입는 경우
        else {
            attacks.shift();
            currentHealth -= attackDamage;
            continuousHealTime = 0;
        }
        currentTime++;
    }
    
    return currentHealth > 0 ? currentHealth :  -1;
}

2번

문제 링크

  • bfs를 통해 석유의 좌표와 양을 확인해 가장 많이 석유를 뽑을 수 있는 구역을 찾으면 된다.
  • bfs 함수에서 각 덩어리별로 Set으로 석유를 캐낼 수 있는 좌표를, 정수로 석유의 양을 전달해준다.
  • 각 위치별로 얼마나 석유를 캘 수 있는지 구해 최대값을 구하면 된다.
  • 아래 코드대로는 시간제한에 걸린다. bfs함수에서 방문할 점을 미리 걸러내야 시간초과없이 문제를 해결할 수 있다.
function solution(land) {
    let totalOilArray = new Array(land[0].length).fill(0);
    
    const isVisited = Array.from({length: land.length}, () => new Array(land[0].length).fill(0));
    
    const bfs = (coordX, coordY) => {
        const includesSet = new Set();
        let oilSize = 0;
        
        const visitArray = [[coordX, coordY]];
        for (let i = 0; i < visitArray.length; i++) {
            const [currentX, currentY] = visitArray[i];
            
            // 석유가 없는 경우
            if (isVisited[currentX][currentY] === 1) continue;
            
            // 석유가 있는경우 bfs
            else {
                isVisited[currentX][currentY] = 1;
                includesSet.add(currentY);
                oilSize++;
                if (currentX > 0);
                if (currentY > 0);
                if (currentX < land.length - 1);
                if (currentY < land[0].length - 1);
            }
        }
        return {set: includesSet, size: oilSize};
    }
    
    // 석유가 있으면 bfs 수행하게 하기
    for(let i = 0; i < land.length; i++) {
        for(let j = 0; j < land[0].length; j++) {
            if(land[i][j] === 1 && isVisited[i][j] === 0) {
                const {set, size} = bfs(i, j);
                set.forEach(target => totalOilArray[target] += size);
            }
            else isVisited[i][j] = 1;
        }
    }
    
    return Math.max(...totalOilArray);
}

3번

문제 링크

  • 분침과는 1분에 1회이상, 초침과는 1시간에 1회 이상 만날 수 없다.
  • 그런데 주어진 시간은 초단위로 주어진다. 1초에 1회이상 만날수 없다는 사실만 기억하자
  • 하루종일 1초단위로 겹치는지 확인해도 84000번밖에 안된다.
  • 시계의 각도를 계산한 다음 1초마다 시침, 분침이 겹치는지 확인해주고, 마지막 시간, 12/24시 정각의 경우만 따로 예외처리를 해주면 된다.
function solution(h1, m1, s1, h2, m2, s2) {
    const hourRadFirst = ((h1 * 60 * 60) + (m1 * 60) + s1);
    const hourRadLast = ((h2 * 60 * 60) + (m2 * 60) + s2);

    // 계산의 편의를 위해 정수로 각도 상대값 변환 (0-43199)
    let hourRad = (((h1 % 12) * 60 * 60) + (m1 * 60) + s1);
    let minRad = ((m1 * 60) + s1) * 12;
    let secRad = s1 * 720;

    let answer = 0;

    for (let i = 0; i < hourRadLast - hourRadFirst; i++) {
        if (hourRad >= secRad && hourRad + 1 < secRad + 720) answer++;
        if (minRad >= secRad && minRad + 12 < secRad + 720) answer++;

        hourRad = (hourRad + 1) % 43200
        minRad = (minRad + 12) % 43200
        secRad = (secRad + 720) % 43200
    }

    // 마지막 부분은 따로 체크
    if (hourRad === secRad) answer++;
    if (minRad === secRad) answer++;

    // 0시, 12시 정각에 시침, 분침, 초침이 동시에 만나기 때문에 포함되어있으면 하나씩 빼주어야 함
    if (hourRadFirst === 0) answer--;
    if (hourRadFirst <= 43200 && hourRadLast >= 43200) answer--;

    return answer;
}

4번

문제 링크

  • 문제 자체는 완전탐색 문제이나, 수레를 동시에 2개씩 옮겨야 한다는 특징이 있다.
  • dfs, bfs 어느 방법으로도 풀 수 있으나, 재귀의 깊이가 깊어야 32이기 때문에 dfs방식을 택했다.
  • dfs 함수에는 현재 각각의 수레위치, 중복 방문 확인을 위한 지금 점 직전까지의 수레가 움직인 기록이 필요하다.
  • dfs 탐색시 움직인 기록이 모순이 되는 경우는 아래와 같다.
    • 벽으로 움직인 경우
    • 두 수레가 겹치는 경우
    • 각 수레가 이전에 방문한 적이 있는 경우
  • 모순이 없다면 빨간색, 파란색 수레 중 하나를 움직여야 한다
    • 처음에는 빨간색, 파란색 양쪽을 다 움직여봐야 한다. (아닐시 테케에서 걸림)
    • 빨간색, 파란색 어느 한쪽이 이동이 완료되었다면, 나머지 한 색상의 수레만 움직일 수 있다.
    • 이동이 둘다 완료되지 않았으면 직전에 움직인 색상과 반대되는 색상의 수레를 움직여주면 된다.
  • 두 수레가 모두 도착했으면 지금까지 이동한 경로를 배열에 push해준다.
  • 경로들 중 가장 최단거리를 구하면 된다.
function solution(maze) {
    let redStartCoord;
    let blueStartCoord;
    
    let redCompleteCoord;
    let blueCompleteCoord;
    
    // 지도에서 벽만 남기고 나머지 데이터는 따로 빼두기
    for (let i = 0; i < maze.length; i++) {
        for (let j = 0; j < maze[0].length; j++) {
            if (maze[i][j] === 0 || maze[i][j] === 5) continue;
            if (maze[i][j] === 1) redStartCoord = [i, j];
            if (maze[i][j] === 2) blueStartCoord = [i, j];
            if (maze[i][j] === 3) redCompleteCoord = [i, j];
            if (maze[i][j] === 4) blueCompleteCoord = [i, j];
            maze[i][j] = 0;
        }
    }
    
    const rootArray = []; // 경로를 저장하는 배열
    
    const dfs = (redCoord, blueCoord, visitArray) => {
        const isRedComplete = redCoord[0] === redCompleteCoord[0] && redCoord[1] === redCompleteCoord[1];
        const isBlueComplete = blueCoord[0] === blueCompleteCoord[0] && blueCoord[1] === blueCompleteCoord[1];
        
        // 벽있는지 확인
        if (maze[redCoord[0]][redCoord[1]] === 5) return;
        if (maze[blueCoord[0]][blueCoord[1]] === 5) return;
        
        // 이미 다른 수레가 있는 곳인지 확인
        if (redCoord[0] === blueCoord[0] && redCoord[1] === blueCoord[1]) return;
        
        // 이미 방문한 곳인지 확인
        if (visitArray.some(([x, y, color]) => x === redCoord[0] && y === redCoord[1] && color === "red")) return;
        if (visitArray.some(([x, y, color]) => x === blueCoord[0] && y === blueCoord[1] && color === "blue")) return;
        
        // 두 수레다 도착한 경우
        if (isRedComplete === true && isBlueComplete === true) {
            rootArray.push(visitArray);
            return;
        }
        
        // 빨간색 수레 움직이는 로직
        const redMove = () => {
            const [redX, redY] = redCoord;
            
            if (redX > 0) dfs([redX - 1, redY], [...blueCoord], [...visitArray, [redX, redY, "red"]]);
            if (redY > 0) dfs([redX, redY - 1], [...blueCoord], [...visitArray, [redX, redY, "red"]]);
            if (redX < maze.length - 1) dfs([redX + 1, redY], [...blueCoord], [...visitArray, [redX, redY, "red"]]);
            if (redY < maze[0].length - 1) dfs([redX, redY + 1], [...blueCoord], [...visitArray, [redX, redY, "red"]]);
        }
        
        // 파란색 수레 움직이는 로직
        const blueMove = () => {
            const [blueX, blueY] = blueCoord;
            
            if (blueX > 0) dfs([...redCoord], [blueX - 1, blueY], [...visitArray, [blueX, blueY, "blue"]]);
            if (blueY > 0) dfs([...redCoord], [blueX, blueY - 1], [...visitArray, [blueX, blueY, "blue"]]);
            if (blueX < maze.length - 1) dfs([...redCoord], [blueX + 1, blueY], [...visitArray, [blueX, blueY, "blue"]]);
            if (blueY < maze[0].length - 1) dfs([...redCoord], [blueX, blueY + 1], [...visitArray, [blueX, blueY, "blue"]]);
        }
        
        // 처음엔 둘다해봐야함
        if (visitArray.length === 0) {
            redMove();
            blueMove();
        }
        
        // 빨간색 옮길 차례
        else if (isBlueComplete === true || (isRedComplete === false && isBlueComplete === false && visitArray[visitArray.length - 1][2] === "blue")) redMove();
        
        // 파란색 옮길 차례
        else blueMove();
    }
    
    dfs(redStartCoord, blueStartCoord, []);
  
    //경로 데이터를 이동한 횟수만 거치게 가공
    const answerArray = rootArray.map(root => {
        const redNum = root.filter(([x, y, result]) => result === "red").length;
        const blueNum = root.length - redNum;
        return Math.max(redNum, blueNum);
    });
    
    return answerArray.length === 0 ? 0 : Math.min(...answerArray)
}
profile
냐아아아아아아아아앙

0개의 댓글