[문제]

 

www.acmicpc.net/problem/2252

 

2252번: 줄 세우기

첫째 줄에 N(1≤N≤32,000), M(1≤M≤100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의미이��

www.acmicpc.net

[코드]

N 명의 학생들을 M번 비교하고 그 결과가 입력으로 주어진다.

이를 통해 학생들을 줄을 세우는 결과를 출력하면 된다.

 

학생들의 키가 모두 주어진 것이 아니고 일부만 주어졌으므로 '위상 정렬'을 사용해서 풀었다.

Degree에 각 노드에 대한 연결 차수를 저장하고

연결 차수가 0인 노드만 queue에 push 하여 순서대로 출력했다.

 

[풀이]

 

시간 : 44ms

 

#include <iostream>
#include <queue>
#include <vector>

#define MAX 32001

using namespace std;

int N,M;
int Degree[MAX]={0,};

vector <int> v[MAX];


int main(void)
{
    queue <int> q;
    
    scanf("%d %d",&N,&M);
    for(int i=0;i<M;i++)
    {
        int x1,x2;
        scanf("%d %d",&x1,&x2);
        
        Degree[x2]++;
        v[x1].push_back(x2);
    }
    
    for(int i=1;i<=N;i++)    if(Degree[i]==0)    q.push(i);
    
    while(!q.empty())
    {
        int x = q.front();
        q.pop();
        
        printf("%d ",x);
        
        for(int i=0;i<v[x].size();i++){
            int tmp = v[x][i];
            Degree[tmp]--;
            if(Degree[tmp]==0)  q.push(tmp);
        }
    }
    return 0;
}

'알고리즘 > 백준' 카테고리의 다른 글

[백준] 3020. 개똥벌레  (0) 2020.10.15
[백준] 11660. 구간 합 구하기5  (0) 2020.10.15
[백준]1463. 1로 만들기  (0) 2020.10.09
[백준] 2003. 수들의 합  (0) 2020.10.07
[백준] 17837. 새로운 게임2  (0) 2020.10.06

[개념]

 

위상 정렬이란 ? 

 

위상 정렬(Topology Sort) 알고리즘이란 순서가 정해진 작업을 차례대로 수행해야 할 때, 순서를 결정하기 위해 사용하는 알고리즘이다.

 

 

하나의 노드에 들어오기 위해 필요한 노드의 개수를 '진입차수'라고 한다.

위의 예제에서 보면 2번 노드의 진입차수는 13,7에 의해 2가 된다.

 

위상 정렬은 방향 그래프에서 진입차수가 0인 노드가 하나 존재해야 된다. 그리고 해당 노드가 위상 정렬의 시작 노드가 된다.

위상 정렬은 진입차수가 0인 노드를 시작으로 순서대로 queue에 넣어준다. 

 

하나의 방향 그래프에서 여러 가지의 위상 정렬이 가능하다.

 

1. 진입차수가 0인 노드를 큐에 넣는다.

2. 큐에서 꺼내고 해당 노드에 부속된 간선 삭제

1,2번을 계속 반복해가면서 모든 노드가 선택되면 종료된다.

 

첫번째로 진입 차수가 0인 노드 5,7을 큐에 넣는다.

 

 

큐에서 노드를 꺼내고 해당 노드와 연결된 간선을 삭제해준 뒤 큐에 넣는다.

 

 

마찬가지로 큐에서 노드를 꺼내고 해당 노드의 간선을 삭제해준 뒤 연결된 노드를 큐에 넣어준다.

 

 

위와 같은 방법으로 계속해서 반복한다.

 

 

 

 

더이상 큐에 넣을 노드가 없으므로 위상 정렬이 끝나고 아래와 같이 순서가 정렬된 것을 확인할 수 있다.

 

[코드]

 

백준에서 위상 정렬 관려 코드로

아래와 같이 위상 정렬을 구현할 수 있다.

(www.acmicpc.net/problem/2252)

 

#include <iostream>
#include <queue>
#include <vector>

#define MAX 32001

using namespace std;

int N,M;
int Degree[MAX]={0,};

vector <int> v[MAX];


int main(void)
{
    queue <int> q;
    
    scanf("%d %d",&N,&M);
    for(int i=0;i<M;i++)
    {
        int x1,x2;
        scanf("%d %d",&x1,&x2);
        
        Degree[x2]++;
        v[x1].push_back(x2);
    }
    
    for(int i=1;i<=N;i++)    if(Degree[i]==0)    q.push(i);
    
    while(!q.empty())
    {
        int x = q.front();
        q.pop();
        
        printf("%d ",x);
        
        for(int i=0;i<v[x].size();i++){
            int tmp = v[x][i];
            Degree[tmp]--;
            if(Degree[tmp]==0)  q.push(tmp);
        }
    }
    return 0;
}

 

[문제]

programmers.co.kr/learn/courses/30/lessons/49994#

 

코딩테스트 연습 - 방문 길이

 

programmers.co.kr

[풀이]

 

입력으로 움직이는 방향이 주어지고 이에 알맞게 움직이면 된다.

걸어간 길의 길이를 출력하면 된다.

 

단, 이미 지나왔던 경로는 포함하지 않는다.

또한 범위를 벗어나는 경우 움직이지 않는다.

 

따라서 좌표와 방향을 가지고 visited 배열을 사용해 이미 방문한 길은 포함하지 않도록 하였다.

 

문제에서 범위가 -5부터 5까지인데 배열은 index가 0부터 가능하므로

(0,0) ~ (10, 10)으로 변경했다.

 

 

[코드]

 

#include <string>

#define MAX 11

using namespace std;

int solution(string dirs) {
    
    int answer = 0;
    int visited[MAX][MAX][4]={0,};
    
    int x=5;
    int y=5;

    int _size = dirs.size();
    
    for(int i=0;i<_size;i++)
    {
        int dir=0;
        int nx=x;
        int ny=y;
        
        switch(dirs[i])
        {
            case 'U':
                ny +=1;
                dir =0;
                break;
            case 'D':
                ny -=1;
                dir=1;
                break;
            case 'R':
                nx +=1;
                dir=2;
                break;
            case 'L':
                nx -=1;
                dir=3;
                break;
        }
        if(nx<0 || ny<0 || nx >10 || ny>10) continue;
        
        if(visited[x][y][dir]==0){
            answer+=1;
            visited[x][y][dir] =1;
            
            if(dir==0)  dir=1;
            else if (dir==1)    dir=0;
            else if(dir==2) dir=3;
            else if(dir==3) dir=2;
            
            visited[nx][ny][dir]=1;
        }
        x=nx;
        y=ny;
    }
    return answer;
}

[문제]

www.acmicpc.net/problem/1463

 

1463번: 1로 만들기

첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.

www.acmicpc.net

[풀이]

DP를 사용해서 2부터 N까지의 1을 만들 때 필요한 연산의 최소 횟수를 구했다.

bottom-up 방식을 사용해 2부터 N까지의 최솟 값을 구했다.

 

연산은 아래와 같이 3가지 연산이 있다.

  1. X가 3으로 나누어 떨어지면, 3으로 나눈다.
  2. X가 2로 나누어 떨어지면, 2로 나눈다.
  3. 1을 뺀다.

따라서 1을 빼는 연산은 모든 숫자에 대해 가능하므로 구하고 

X가 2로 나눠지는 경우, 3으로 나눠지는 경우에는 이에 대한 경우도 구해서 최솟 값을 구했다.

 

 

[코드]

 

시간 : 4ms

#include <stdio.h>
#define MAX 1000001


int MIN(int a, int b)
{
    if(a>b )    return b;
    else    return a;
}

int main(void)
{
    int N;
    int DP[MAX]={0,};
    
    scanf("%d",&N);
    
    for(int i=2;i<=N;i++)
    {
        DP[i] = DP[i-1]+1;
        if(i%2 == 0)    DP[i] = MIN(DP[i], DP[i/2]+1);
        if(i%3 == 0)    DP[i] = MIN(DP[i], DP[i/3]+1);
    }
    printf("%d",DP[N]);
    return 0;
    
}

'알고리즘 > 백준' 카테고리의 다른 글

[백준] 11660. 구간 합 구하기5  (0) 2020.10.15
[백준] 2252. 줄 세우기  (0) 2020.10.13
[백준] 2003. 수들의 합  (0) 2020.10.07
[백준] 17837. 새로운 게임2  (0) 2020.10.06
[백준] 17822. 원판 돌리기  (0) 2020.10.06

[개념]

 

 

 

구간 합을 구할 때 사용되는 알고리즘으로 배열의 길이가 짧은 경우 이중 for문을 사용해서 구하면 되지만

배열의 길이가 길어지면 시간 초과가 발생하므로 투 포인터 알고리즘을 사용한다.

 

 

아래 코드에서 보면  N은 배열의 크기,  M은 구하고자 하는 합이다.

구간의 합이 M을 만족하는 경우의 수를 구한다고 하자.

 

start, end 모두 배열의 첫번째부터 시작한다.

 

- M = sum

구간의 합이 M과 같다면 경우의 수 total에 1을 추가한다.

그리고 end를 한칸 뒤로 움직인다.

 

- M < sum

현재 구간의 합이 구하고자 하는 구간의 합보다 크다면 start를 1씩 증가시켜 주면서 구간의 범위를 좁혀준다.

이 때 start가 end보다 커지게 되는 경우 start, end를 같은 위치로 처리한다.

 

- M > sum

현재 구간의 합이 구하고자 하는 구간의 합보다 작다면 end를 한칸 뒤로 움직여 구간의 범위를 넓혀준다.

 

 

[코드]

 

    int start =0 ;
    int end =0;
    int sum =arr[0];
    
    while(start <= end && end < N)
    {
        if(sum < M) sum += arr[++end];
        else if( sum > M)
        {
            sum -= arr[start++];
            if( start > end && start <N){
                end=  start;
                sum= arr[start];
            }
        }
        else if (sum == M)
        {
            total++;
            sum += arr[++end];
        }
    }

[문제]

www.acmicpc.net/problem/2003

 

2003번: 수들의 합 2

첫째 줄에 N(1≤N≤10,000), M(1≤M≤300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], …, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.

www.acmicpc.net

[풀이]

 

N,M이 주어지고 길이가 N인 배열에서 구간합이 M인 경우의 수를 찾아서 출력하는 문제

구간합 문제길래 이중 for문을 사용해서 풀려고 했다.

하지만 시간 제한이 0.5초인데 M의 범위가 1부터 3억이라서 이중 for문을 사용하면 시간초과가 발생한다.

 

따라서 구간합 구하는 알고리즘인 투 포인터 알고리즘을 사용해서 풀었다.

start , end 를 배열의 첫번째 index부터 시작해서

현재 구간 합이  M보다 작으면 end를 +1, M보다 크면 start를 +1 

이런 방식으로 start, end 의 index를 조절하면서 구간 합을 구했다.

 

 

 

[코드]

 

시간 : 0ms

 

#include <iostream>
#define MAX 10000
using namespace std;

int N,M;
int arr[MAX];


int main(void)
{
    int total=0;

    scanf("%d %d",&N,&M);
    for(int i=0;i<N;i++)    scanf("%d",&arr[i]);
    
    int start =0 ;
    int end =0;
    int sum =arr[0];
    
    
    while(start <= end && end < N)
    {
        if(sum < M) sum += arr[++end];
        else if( sum > M)
        {
            sum -= arr[start++];
            if( start > end && start <N){
                end=  start;
                sum= arr[start];
            }
        }
        else if (sum == M)
        {
            total++;
            sum += arr[++end];
        }
    }
    
    printf("%d",total);
    return 0;
    
}

'알고리즘 > 백준' 카테고리의 다른 글

[백준] 2252. 줄 세우기  (0) 2020.10.13
[백준]1463. 1로 만들기  (0) 2020.10.09
[백준] 17837. 새로운 게임2  (0) 2020.10.06
[백준] 17822. 원판 돌리기  (0) 2020.10.06
[백준]1920. 수 찾기  (0) 2020.10.06

[개념]

 

크루스칼 알고리즘은 

모든 노드들을 최소 비용으롤 연결하는 알고리즘이다.

N개의 노드를 N-1개의 간선으로 연결 

 ex) 모든 도시를 도로로 연결할 때 가장 적은 비용으로 연결하는 방법

 

1. 노드 간의 연결 정보를 저장하고 비용을 기준으로 오름차순으로 정렬한다.

2. 비용이 적은 것부터 연결 

이 때 사이클이 발생하지 않도록 주의

==> Union-Find 를 사용해 확인, 노드를 연결할 때 부모가 같다면 이미 연결된 것이므로 제외

3. 현재 간선을 현재  MST 집합에 추가 

 

 

아래 그림과 같이 가중치를 기준으로 오름차순으로 정렬한다.

가중치가 작은 것부터 선택하여 노드를 서로 연결한다.

해당 예제에서는 2번 노드와 5번 노드를 연결한다.

 

(아래 그림에서 방향 그래프로 그렸는데 잘못 그렸습니다. 크루스칼 알고리즘에서는 방향이 없습니다. ) 

 

 

그 다음으로 가중치가 작은 6번 노드와 1번 노드를 연결한다.

 

 

 

 

이제 5->4번을 연결해준다.

 

 

 

다음으로 4->2를 연결해야 하는데

연결하게 되는 경우 사이클이 발생한다.

따라서 연결하지 않고 다음으로 넘어가서 4->3을 연결한다.

 

 

 

 

 

마지막으로 3->6까지 연결하면 5개의 간선 ( N-1)으로 N개의 노드를 모드 연결할 수 있다.

이와 같은 방법으로 MST를 구하는 것을 크루스칼 알고리즘이라고 한다.

 

 

 

[코드]

 

노드가 총 5개라고 하자.

node배열에는 node간의 연결 정보를 저장한다.

처음에는 각 노드에 대해 부모를 자기 자신으로 초기화한다.

vector에는 간선 길이와 node 배열에서의 index를 저장한다.

 

즉 node[0] = {1,2};는 1번 노드와 2번 노드를 연결한 것이다.

또한 v[0]={10,0}에는 0번 index에 대한 간선의 길이가 저장되어있다. 

따라서 1번 노드와 2번 노드 사이의 간선 길이는 10이라는 것을 알 수 있다.

 

노드간의 연결정보를 저장하고  Kruskal 함수에서 간선 길이를 기준으로 오름차순으로 정렬한다.

이제 길이가 짧은 간선부터 각 노드간 연결을 수행할 지 확인한다.

이 때 Find 함수를 통해 각 노드의 parent가 같은지 확인한다.

Parent가 같다는 것은 이미 연결이 된 것이고 , 여기서 또 Union을 하면 Cycle이 발생한다.

따라서 부모가 같은 경우에는 이미 연결이 되었기 때문에 Union을 수행하지 않는다.

 

이와 같이 간선의 길이를 기준으로 짧은 것부터 연결을 하고

마지막에 Find 함수를 통해서 각 노드의 parent를 통해 모든 노드가 연결이 되었는지 확인할 수 있다.

(parent가 하나라도 다르면 모두 연결되지 않을 것)

 

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct Node{
    int start, end;
};
Node node[10];
vector <pair<int,int>> v;// len, node number

int parent[10];

int Find(int idx)
{
    if(idx == parent[idx] ) return idx;
    else{
        parent[idx] = Find(parent[idx]);
        return parent[idx];
    }
}

void Union(int idx1 , int idx2)
{
    idx1 = Find(idx1);
    idx2 = Find(idx2);
    
    if(idx1 != idx2)    parent[idx2] = idx1;
    
}

int Kruskal()
{
    int ans =0;
                            
    sort(v.begin(),v.end());
    
    for(int i=0;i<v.size();i++)
    {
        int len = v[i].first;
        int idx = v[i].second;
       
        if(Find(node[idx].start) == Find(node[idx].end))    continue;
        Union(node[idx].start , node[idx].end);
        ans += len;
    }
    
    int num = Find(1);
    for(int i=2;i<=5;i++){
        if(Find(i) != num)  return -1;
    }
    return ans;
}

int main(void)
{
    for(int i=1;i<=5;i++)   parent[i]=i;
    
    node[0] = {1,2};
    node[1] = {1,3};
    node[2] = {2,3};
    node[3] = {2,5};
    node[5] = {3,5};
    node[6] = {4,5};
    node[7] = {3,4};
    node[8] = {1,4};
    
    v.push_back({10,0});
    v.push_back({4,1});
    v.push_back({7,2});
    v.push_back({13,3});
    v.push_back({2,4});
    v.push_back({42,5});
    v.push_back({33,6});
    v.push_back({5,7});
    v.push_back({17,8});
    
    Kruskal();

}

 

크루스칼 알고리즘에 대해 좀더 자세한 내용은 아래 링크를 통해 확인할 수 있습니다.

https://blog.naver.com/ndb796/221230967614

 

17. Union-Find(합집합 찾기)

  Union-Find(유니온-파인드)는 대표적인 그래프 알고리즘입니다. 바로 '합집합 찾기'라는 의미를 ...

blog.naver.com

[문제]

www.acmicpc.net/problem/17837

 

17837번: 새로운 게임 2

재현이는 주변을 살펴보던 중 체스판과 말을 이용해서 새로운 게임을 만들기로 했다. 새로운 게임은 크기가 N×N인 체스판에서 진행되고, 사용하는 말의 개수는 K개이다. 말은 원판모양이고, 하�

www.acmicpc.net

[풀이]

시뮬레이션 문제로 문제 조건 잘 지켜가면서 풀었다.

 

하나의 turn 마다 1번 말부터 순서대로 움직인다.

한 칸에 여러 말이 쌓여 있을 수 있고 이동하려는 말 위에 다른 말이 존재한다면 같이 움직인다.

한 칸에 말이 4마리 이상 쌓일 경우 게임이 종료된다.

 

 

이동하려는 칸이 흰색인 경우 이동하려는 말의 위에 있는 말부터 쌓이기 시작한다.

이동하려는 칸이 빨간색일 경우 이동하려는 말부터 쌓이기 시작한다.

이동하려는 칸이 파란색일 경우 반대 방향으로 바꾸고 움직인다.

방향을 바꿔도 파란색이라면 움직이지 않는다.

범위를 벗어나는 경우도 파란색의 경우와 동일하게 한다.

 


solve 함수에서 1번 말부터 움직이고

이동하려는 칸의 색깔에 알맞게 white, red, blue에 맞는 함수를 호출했다.

blue인 경우 방향을 바꾸고 움직여야 하므로 동일하게 move 함수를 호출한다.

red , white의 경우 말이 움직이는 순서만 다를 뿐 다른 것은 같으므로

move 함수 하나로 처리하고 말이 쌓일 때만 white인지 red 인지 구분하여 쌓이는 순서를 다르게 해줬다.

 

 

[코드]

 

시간 : 0ms

 

#include <iostream>
#include <vector>

#define MAX 12

using namespace std;

int N,K;
int color[MAX][MAX]={0,};
vector<int> map[MAX][MAX];

int dx[4] = {0,0,-1,1};
int dy[4] = {1,-1,0,0};

struct HORSE{
    int x,y,dir;
};
HORSE horse[10];

bool move(int col , int idx)
{
    int x = horse[idx].x;
    int y = horse[idx].y;
    int dir = horse[idx].dir;
    
    int nx = x+dx[dir];
    int ny = y+dy[dir];
    
    
    vector<int> v;
    int _size = map[x][y].size();
    
    while(1)
    {
        int num = map[x][y][_size-1];
        _size--;
        map[x][y].pop_back();
        v.push_back(num);
        if(num==idx)    break;
    }
    
    if(col ==0){
        for(int i=v.size()-1;i>=0;i--)
        {
            int num = v[i];
            map[nx][ny].push_back(num);
            horse[num].x= nx;
            horse[num].y = ny;
        }
    }
    else {

        for(int i=0;i<v.size();i++)
        {
            int num = v[i];
            map[nx][ny].push_back(num);
            horse[num].x= nx;
            horse[num].y = ny;
        }
    }
    
    if(map[nx][ny].size() >= 4) return false;
    return true;
}


bool blue(int idx)
{
    bool res = true;
    
    int x = horse[idx].x;
    int y = horse[idx].y;
    int dir = horse[idx].dir;
    
    int nx = x+dx[dir];
    int ny = y+dy[dir];

    if(nx< 0 ||  ny < 0  || nx >= N || ny >= N || color[nx][ny]==2)   return true;
    else if(color[nx][ny]==0)  res = move(0,idx);
    else if(color[nx][ny]==1) res = move(1,idx);
    
    return res;
    
}

bool solve()
{
    
    for(int i=0;i<K;i++){
        bool res=true;
        int x = horse[i].x;
        int y = horse[i].y;
        int dir = horse[i].dir;
        
        int nx = x+dx[dir];
        int ny = y+dy[dir];
        
        if(nx< 0 ||  ny < 0  || nx >= N || ny >= N || color[nx][ny]==2){
            
            if(dir ==0) horse[i].dir=1;
            else if(dir ==1) horse[i].dir=0;
            else if(dir ==2) horse[i].dir=3;
            else if(dir ==3) horse[i].dir=2;
            res = blue(i);
        }
        else if(color[nx][ny]==0)  res = move(0,i);
        else if(color[nx][ny]==1) res = move(1,i);
        
        if(res==false)  return false;
    }
    return true;
}
    
int main(void)
{
    scanf("%d %d",&N,&K);
    
    for(int i=0;i<N;i++){
        for(int j=0;j<N;j++){
            scanf("%d",&color[i][j]);
        }
    }
    
    for(int i=0;i<K;i++){
        int x,y,dir;
        scanf("%d %d %d",&x,&y,&dir);
        horse[i] = {x-1,y-1,dir-1};
        map[x-1][y-1].push_back(i);
    }
    
    int turn=0;
    
    while(1){
        turn++;
        if(!solve())    break;
        else if(turn > 1000){
            printf("-1");
            return 0;
        }
    }
    printf("%d",turn);

    return 0;
    
}

'알고리즘 > 백준' 카테고리의 다른 글

[백준]1463. 1로 만들기  (0) 2020.10.09
[백준] 2003. 수들의 합  (0) 2020.10.07
[백준] 17822. 원판 돌리기  (0) 2020.10.06
[백준]1920. 수 찾기  (0) 2020.10.06
[백준] 1976. 여행가자  (0) 2020.10.02

+ Recent posts