코테 기초

Search

서버관리자 페페 2022. 12. 16. 22:59

 

001 (DFS)

public class Main {

    static ArrayList<Integer>[] F;
    static boolean[] visited;

    public static void main(String[] args) throws IOException {

- 같은 클래스 안, 다른 모듈들에서 동시에 사용가능하게 변수 인식

- static으로 한다

 

 

002

public class Main {

    // recognizer + excess modifier
    static ArrayList<Integer>[] F;
    static boolean[] visited;

    public static void main(String[] args) throws IOException {
        
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int C = Integer.parseInt(st.nextToken());
        int M = Integer.parseInt(st.nextToken());
        
        // Capacity
        F = new ArrayList[C+1];

        // initializer
        for (int i = 1; i <= C; i++) {
            F[i] = new ArrayList<>();
        }

        // value allocator
        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int s = Integer.parseInt(st.nextToken());
            int e = Integer.parseInt(st.nextToken());
            F[s].add(e);
            F[e].add(s);

        }

> DFS 시 에지-노드 그래프를 나타내기 위해 인접 리스트를 사용하며

> 에지 개수만큼 밸류 할당

> "인식 > 캐퍼시티 > 초기화 > 할당"

 

 

 

003

// Operating DFS + marker
int marker = 0;
for (int i = 1; i <= N; i++) {
    if (!visited[i]) {
        marker++;
        DFS(i);
    }
}
// External Module : DFS
static void DFS(int v) {
    
    // EP
    if (visited[v]) {
        return;
    }
    
    // Phaser
    visited[v] = true;
    for (int i : F[v]) {
        if (!visited[i]) {
            DFS(i);
        }
    }
}

DFS

  • 1 : 일단 linear for문으로 모든 요소를 DFS 실행하나, if (!visited[i]) phaser를 추가하여 거른다
  • 2 : DFS가 시작되면 if(visited[v) return phaser 로 우선 방문된 인자로 중복에 걸리는지 확인한다, 이것은 1번에서 시작되는 요소를 거르기 위한 게 아닌, 아래에서 실행되어 나오는 connected를 거르기 위한 것
  • 3 : 해당 인자에 대해 phaser switch 켜준다
  • 4 : connected node를 차례로 DFS 해주나, 방문되지 않을 때만 실행한다

Marker

  • DFS에서 connected 횟수가 아닌, 연결 요소이므로 하나의 DFS가 모두 끝날때까지 1회, 즉 시작될 때 1번만 체크하면 된다

 

- 재귀에 걸리는 DFS 같은 경우는 외부에서 1, 내부에서 1 들어오는 양방향 phaser로 예외 처리를 한다

- 클래스 내 외부 모듈 사용 : static void, 그렇지만 recursive를 turn 하기 위해 return이 사용됨

- 재귀 부메랑이 다시 돌아올 때는 EP에 걸렸을 때

- F[v] 는 connected edge임

- boolean visited의 초깃값은 들어가지 않는다

 

 

004

static boolean isPrime(int num) {
    for (int i = 2; i <= num / 2; i++) {
        if (num % i == 0)
            return false;
    }
    return true;
}

> 소수 판별 함수

> 모든 자연수로 나눴을때 살아남는게 목적

> 1 ~ N/2 // N/2+1 ~ N // N+1 (1~4, 5~8, 9)

> 첫번째 파트로 안나눠지면, 두번째 파트로도 안나눠진다는 것은 동일하기에 i < num이 아닌 i <= num / 2 를 사용하여 선혈 공간 줄임 

 

 

 

 

006

static void DFS(int number, int digit) {

    // Quick Recovery PipeLine
    if (digit == N) {
        if (isPrime(number)) {
            System.out.println(number);
        }
        return;
    }

    for (int i = 1; i < 10; i++) {
        if (i % 2 == 0) {
            continue;
        }
        if (isPrime(number * 10 + i)) {
            DFS(number * 10 + i, digit++);
        }
    }
}

> if의 종류

  • thesis Pipeline
  • Quick Recovery Pipeline(checker 충족시)
  • Exceptional Pipeline (phaser)

> 해당 if는 checker 2 + OEC로 구성

> QRP 후 재귀에서 return 잊지 말기

> 아래 DFS 진행은, 각 단계를 저장하는 게 아닌, 소수이기만 하면 다음 단계로 나아가 끝에 도달하는 기능만 해주면 된다

 

 

007

// Operating DFS
for (int i = 0; i < N; i++) {
    
    // action
    DFS(i, 1);
    
    // QRP
    if (arrived)
        break;

}

해당 QRP는 action 밑에 오는데,

> 처음은 무조건 depth가 1이라 안 되고

> for문의 다음 시행공간에 들어가기 전에 확인해서 break 해야 하기 때문이다

조건 충족시 바로 break되도록 할것

 

 

008

// OEC
if (arrived)
    System.out.println("1");
else
    System.out.println("0");

> int 아닌 string으로 출력

 

 

009

// External Moduel : DFS + checker
public static void DFS(int now, int depth) {

    // QRP
    if (depth == 5 || arrived) {
        arrived = true;
        return;
    }
    
    // action
    visited[now] = true;
    for (int i : F[now]) {
        if (!visited[i]) {
            DFS(i, depth++);
        }
    }
    
    // Handling Exception
    visited[now] = false;
}

Nonconserved :

  • 문제의 특성상 완전 탐색하는 게 아님
  • 한 노드에서 어디까지(4번 connected == depth:5) 갈 수 있는지 알아보기 위해
  • 일반 DFS처럼 한 노드의 탐색 결과를 보존하지 말고, 다음 connected node가 될 수 있도록 false로 꼭 바꿔준다

 

D에서 순환하여 A로 가는 결과는 없는지?

  • !visited로 거르기 때문에 없다

 

checker / phaser가 왜 2개나 필요한지?

  • depth는 단일 노드의 결과를 반영한다
  • arrived는 아직 탐색이 안된 다음 노드에서, 이미 전 노드가 depth에 도달한 적이 있으니 중복해서 탐색하지 말란 소리다
  • 만약 문제가 각 노드별로 depth 5를 충족하는 것을 찾아내라고 하면 arrive는 없어져야 함
  • visited phaser는 비보존, arrived checker는 보존

 

phaser 는 특수 공간에서 위상값( 존재 or 음으로 사라짐 ) or 방문하거나 방문되지 않음

 

 

010(BFS)

// PRe
for (int i = 1; i <= E; i++) {
    Collections.sort(Connected[i]);
}

> 낮은 노드부터 탐색하기 위해 오름차순 정렬, 까먹지 말기

> 어레이 소트가 아닌 컬렉션 소트에 주의

 

 

 

011

// Operating DFS
visited = new boolean[N+1];
DFS(S);
System.out.println();

// Operating BFS
visited = new boolean[N+1];
BFS(S);
System.out.println();

 

> 각 탐색 실행하기 전 visited[] 초기화 해주고

> 줄 바꿔주기

 

 

012

// EM  : BFS + OEC
private static void BFS(int Node) {
    Queue<Integer> queue = new LinkedList<>();
    queue.add(Node);
    visited[Node] = true;

    while (!queue.isEmpty()) {
        int now_Node = queue.poll();
        System.out.println("now_Node" + " ");
        for (int i : Connected[now_Node]) {
            if (!visited[i]) {
                visited[i] = true;
                queue.add(i);
            }
        }
    }
}

DFS

  • 그대로 Node를 받아서 사용하지만 

BFS

  • queue에 넣고, poll 될때 connected 탐색을 시작한다
  • 이때 넣는것과 빠지는 노드가 다르므로(FIFO), add와 poll 둘다 phaser에 기록한다
  • 탐색결과는 poll 후 하나만 하면 됨
  • 추가 : poll 넣을 박스 준비할 것

 

 

013

//setup
static int[] dx = {0, 1, 0, -1};
static int[] dy = {1, 0, -1, 0};
static boolean[][] visited;
static int[][] Connected;
static int xC, yC;

2차원 탐색에서의 setup은 x y 좌표로 사용하기 위해

 

 

 

014

for (int i = 0; i < xC; i++) {
    st = new StringTokenizer(br.readLine());
    String line = st.nextToken();
    for (int j = 0; j < yC; j++) {
        Connected[i][j] = Integer.parseInt(line.substring(j, j+1));
    }
}

> String으로 된(공백없는) 수열 쪼개서 matrix에 넣기

 

 

015

// EM : BFS
public static void BFS(int i, int j) {

    // Setup
    Queue<int[]> queue = new LinkedList<>();
    queue.offer(new int[] {i, j});
    visited[i][j] = true;
    
    // action
    while (!queue.isEmpty()) {
        int now[] = queue.poll();
        for (int k = 0; k < 4; k++) {
            int x = now[0] + dx[k];
            int y = now[1] + dy[k];
            if (x >= 0 && x < xC && y >= 0 && y <yC) {
                if (Connected[x][y] != 0 && !visited[x][y]) {
                    visited[x][y] = true;
                    Connected[x][y] = Connected[now[0]][now[1]] + 1;
                    queue.add(new int[] {x, y});
                }
            }
        }

    }
}
// 선언과 동시에 배열의 크기 지정 및 값 초기화
int[] arr = {1,2,3,4,5};
int[] arr = new int[]  {1,3,5,2,4};
int[] odds = {1,3,5,7,9};
String[] weeks = {"월","화","수","목","금","토","일"};

2차원에서 BFS 실행

> ArrayList는 필요없다

> new int[] {i, j} 가 무엇인지 알아낼 것

> x y는 좌표로 쓰이는 것

connected는 여기서 depth로 쓰임

 

-

 

016

class Edge {

    int e;
    int weight;

    public Edge(int e, int weight) {
        this.e = e;
        this.weight = weight;
    }
}

> ArrayList<Integer>(); 가 아닌

> 가중치(거리) 정보 있는 Edge 로 사용하기 위해

 

-

 

017

// L2~
Connected = new ArrayList<Integer>();
for (int i = 1; i <= N; i++) {
    Connected[i] = new ArrayList<>();
}
for (int i = 0; i < N; i++) {
    int s = sc.nextInt();
    while (true) {
        // delivery box
        int e = sc.nextInt();
        if (e == -1)
            break;
        int w = sc.nextInt();
        Connected[s].add(new Edge(e, w));


    }
}

더 자각하지 않아도 되는 것

  • delivery box는 전달되기만 하면 더 이상 신경쓰지 않아도 됨

왜 if(-1) 이 중간에 들어가 있지?

  • 일단 자각할 박스가 필요하니, int e 로 인풋을 받아봐야 판단 가능한 것
  • 받았다 해도, Field에 저장되지 않으니 괜찮다

Edge class를 만들어 사용할 때

  • 앞에 꼭 new를 붙인다

 

-

 

 

018

// EM : BFS
private static void BFS(int Node) {

    // Setup
    Queue<Integer> queue = new LinkedList<>();
    queue.add(Node);
    visited[Node] = true;

    // action
    while (!queue.isEmpty()) {
        int now_node = queue.poll();
        for (Edge i : Connected[now_node]) {
            int e = i.e;
            int w = i.weight;
            if (!visited[e]) {
                visited[e] = true;
                queue.add(e);
                distance[e] = distance[now_node] + w;
            }
        }
    }
}

 

 

Edge class를 사용할 때

  • int i 가 아닌 Edge i임
  • int e = i.e; int w = i.weight;로 값을 로드해야 사용가능
  • 마지막 visited에 i를 넣어주는 게 아닌, e를 넣어줘야 함
  • distance를 제외하곤 나머지 그대로 e 사용하면 됨

> 기본 distance 값은 0으로 설정되어 있나?

 

-

 

019

// Operation BFS + distance
phaser = new boolean[N+1];
distance = new int[N+1];
BFS(1);


// Max flip
int Max = 1;
for (int i = 2; i <= N; i++) {
    if (distance[Max] < distance[i]) {
        Max = i;
    }
}


// Operation BFS + distance
phaser = new boolean[N+1];
distance = new int[N+1];
BFS(Max);


// OEC
Arrays.sort(distance);
System.out.println(distance[N]);

- max flip시, 맨 첫번째 값을 정하고, 두번째부터 for문을 돌린다

> 새로 BFS 할 때, 거리랑 방문 리셋 잊지 말것

> 마지막에서 for문을 굴리면서 max를 찾기보다, 그냥 오름차순 정렬 후 맨 오른쪽 값이 max다

 

-

 

020(Binary Search)

// L4~
for (int i = 0; i < tC; i++) {

    // setup
    int s = 0;
    int e = C-1;
    int target = sc.nextInt();
    boolean checked = false;

    // binary search
    while (s <= e) {
        int m = (s + e) / 2;
        if (F[m] > target) {
            e = m - 1;
        } else if (F[m] < target) {
            s = m + 1;
        } else {
            checked = true;
            break;
        }
    }

    // OEC
    if (checked)
        System.out.println(1);
    else
        System.out.println(0);
}

> target은 value이고, m은 cursor이므로 F[m]과 target을 비교해야 함에 주의

 

 

-

 

021

// L2 + setup
int[] F = new int[C];
int s = 0;
int e = 0;
for (int i = 0; i < C; i++) {
    F[i] = sc.nextInt();
    if (s < F[i])
        s = F[i];
    e += F[i];
}

> cursor가 아닌, 블루레이 용량을 탐색해야 하므로 최솟값인 9와 모든 레슨크기를(1~9) 더한 45 사이에서 탐색한다

 

-

 

 

022

int m = (s + e) / 2;
int plate = 0;
int marker = 0;

for (int i = 0; i < C; i++) {

    if (plate + F[i] > m) {
        marker++;
        plate = 0;
    }

    plate += F[i];

}

- plate는 F[i]의 범위들을 오름차순으로 묶는 것인데,

- 가령 1에서 6까지 27이라는 Plate에 담겼지만, 7으로 넘어갈 때 1에서 7까지는 plate 초과라 해당 plate는 리셋

- 7부터는 새 plate에 담기 시작한다

 

-

 

023

if (plate != 0)
    marker++;

if (marker > M) {
    s = m + 1;
} else {
    e = m - 1;
}
// OEC
System.out.println(s);

- 그리고 해당 배열의 끝 인자가 용량을 초과해서 하나가 더 담겼으면 plate 갯수를 하나 더 늘려주고

- 만약 접시 갯수가 주어진 것보다 초과이면, 용량을 m보다 큰데서 찾고

- 적거나 같으면 m보다 작은데서 찾는다

- 그리고 출력되는 것은 s 인덱스 

- 접시 갯수는 3개만 써야 하고, 그 와중의 용량의 최솟값이니 s <= e 때까지 진행되다가, s가 e보다 커지는 순간(최솟값) 출력

 

-

 

 

024

기본 논지

- 작은 수의 갯수가 k-1개인 중앙값이 정답

- 2차원 배열에서 N행이 N의 배수로 구성되어 있으므로, 2차원 배열에서의 k번째 수는 k를 넘지 않는다

- 따라서 2차원 배열의 1~k번쨰 안에 정답이 있다 > 시작 Index = 1, end index = k

 

 

-

 

024

for (int i = 1; i <= C; i++) {
    marker += Math.min(m / i, C);
}

작은 값을 비교하며 카운터

 

 

-

 

 

025

//setup
long s = 1;
long e = K;
long ans = 0;

// binary search
while (s <= e) {

    long m = (s + e) / 2;
    long marker = 0;

    for (int i = 1; i <= C; i++) {
        marker += Math.min(m / i, C);
    }
    
    if (marker < K) {
        s = m + 1;
    } else {
        ans = m;
        e = m - 1;
    }
}

// OEC
System.out.println(ans);

'코테 기초' 카테고리의 다른 글

Number Theory  (0) 2022.12.20
Greedy  (0) 2022.12.20
Sort  (1) 2022.12.16
자료구조 재방문  (0) 2022.12.16
Radix Sort  (0) 2022.12.11