본문 바로가기
백준

[백준, Java] 13505번, 두 수 XOR(트라이, 누적합)

by 열정적인 이찬형 2023. 12. 2.

문제 링크

 

13505번: 두 수 XOR

N개의 수가 주어졌을 때, XOR한 값이 가장 큰 두 수를 찾는 프로그램을 작성하시오. 즉, A1, A2, ..., AN 중에서 i ≠ j이면서 Ai XOR Aj 가 가장 큰 것을 찾아야 한다.

www.acmicpc.net


주의사항

  • JAVA를 사용하여 프로그램을 사용하였습니다.
  • 백준에서 코드를 작성하였을 때 아래 형태에서 Main에서 결과가 출력되어야 합니다.
public class Main{ 	
	public static void main(String[] args){
    }
}


문제 설명


 

접근 방법

이 문제에 핵심

1. N개의 음이 아닌 정수가 주어집니다.

2. N개의 정수 중 2개를 선택해서 XOR을 하였을 때 최대값을 결과로 출력합니다.

 

알고리즘 진행 순서.

 

1. 입력된 정보를 저장합니다.

 

2. 트라이 자료구조를 N개의 수를 저장하면서 최대값을 탐색합니다.

 

3. 탐색해서 얻은 최대값을 결과로 출력합니다.

 

 

트라이 자료구조를 이용하여 N개의 수 적립

 

트라이에 대한 기본적인 지식이 없으시다면, 아래에 내용을 정독하시는 것을 추천드립니다.

 

우리는 N개의 수에 대해서 XOR을 진행할 것이기 때문에 N개의 수를 Bit 형태로 저장할 것입니다.
 
예를 들어, 7이라는 값은
 
111의 값이 저장될 것입니다.
 
하지만, 저희가 최대값을 찾아야 하기 때문에 N이 주어지는 최대값 1,000,000,000을 수용할 수 있는 Bit을 기반으로 작업할 것입니다.
 
결과적으로 111의 값이 들어가는 형태 : 0000000000000000000000000000111
 

 

 

트라이 자료구조를 이용하여 XOR의 최대값 비교해보기

 

N개의 수를 저장할 때마다 현재까지 Trie에 저장된 값들과 비교를 진행합니다.

 

저장할 때마다 비교를 진행할 수 있는 이유는

 

4개의 수를 진행할 때, 위 그림처럼 모든 경우를 비교할 수 있기 때문입니다.

 

XOR에 대한 최대값 비교하기

 

XOR의 최대값이 되려면 해당 값에 Bit와 반대가 되어야 큰 값을 이룰 수 있습니다.
 
예를 들어, 10001에 대한 XOR을 진행해서 최대값이 되려면
 
10001의 반대값 01110이 되어야 합니다.
 
10001 XOR 01110 = 11111으로 최대값을 구성할 수 있습니다.

 

Trie에 저장된 값들을 비교할 때
 
현재 비트의 값 : 1 
 
▶ 현재 Trie의 자식 노드에서 0을 가리키는 노드가 존재하는지 확인
 
       0을 가리키는 노드가 존재하면 해당 노드로 이동
 
     ▷  0을 가리키는 노드가 존재하지 않으면 1을 가리키는 노드로 이동
 
 
현재 비트의 값 : 0
 
▶ 현재 Trie의 자식 노드에서 1을 가리키는 노드가 존재하는지 확인
 
     ▷  1을 가리키는 노드가 존재하면 해당 노드로 이동
 
     ▷  1을 가리키는 노드가 존재하지 않으면 0을 가리키는 노드로 이동
 
 
 
위 비교 과정을 통해서 최대값을 결과로 출력하면 됩니다.

 

※예제입력 과정을 확인하시면 이해하시기 편하실 것입니다.

 

비슷한 문제를 풀어보시려면 아래의 문제를 추천드립니다.

 

13504번: XOR 합

각각의 테스트 케이스마다 수열 A의 연속된 부분 수열 중에서 XOR 합이 가장 큰 부분 수열의 XOR 합을 출력한다.

www.acmicpc.net

 

예제입력 1.

 

1. 입력된 정보를 저장합니다.

 

N : 5
 
 
1 2 3 4 5
Bit 1 10 11 100 101
 
 
 
 

 

2. 트라이 자료구조를 N개의 수를 저장하면서 최대값을 탐색합니다.

 

 
첫 번째 수 : 1(1)을 Trie 저장
 
 
첫 번째 수는 두 수를 XOR할 수 없기 때문에 비교하는 과정을 생략합니다.
 
두 번째 수 : 2(10)을 Trie 저장
 
 
XOR 비교하기
 
2 ▶ 0000000000000000000000000000010
 
최상의 값 : 1111111111111111111111111111111111101
 
하지만, 현재의 값에서 트라이에 저장된 값에 Trie에 대한 비교를 진행하면
 
0000000000000000000000000000001 가장 XOR의 큰 값을 얻을 수 있습니다.
 
 
0000000000000000000000000000010 XOR 0000000000000000000000000000001
 
0000000000000000000000000000011 = 3
 

 

세 번째 수 : 3(11)을 Trie 저장

 

XOR 비교하기
 
3 ▶ 0000000000000000000000000000011
 
최상의 값 : 1111111111111111111111111111111111100
 
하지만, 현재의 값에서 트라이에 저장된 값에 Trie에 대한 비교를 진행하면
 
0000000000000000000000000000001 가장 XOR의 큰 값을 얻을 수 있습니다.
 
0000000000000000000000000000011 XOR 0000000000000000000000000000001
 
 0000000000000000000000000000010 = 2

 

네 번째 수 : 4(100)을 Trie 저장

XOR 비교하기
 
4 ▶ 0000000000000000000000000000100
 
최상의 값 : 1111111111111111111111111111111111011
 
하지만, 현재의 값에서 트라이에 저장된 값에 Trie에 대한 비교를 진행하면
 
0000000000000000000000000000011 가장 XOR의 큰 값을 얻을 수 있습니다.
 
0000000000000000000000000000100 XOR 0000000000000000000000000000011
 
 0000000000000000000000000000111 = 7

 

다섯 번째 수 : 5(101)을 Trie 저장

 

 

XOR 비교하기
 
5 ▶ 0000000000000000000000000000101
 
최상의 값 : 1111111111111111111111111111111111010
 
하지만, 현재의 값에서 트라이에 저장된 값에 Trie에 대한 비교를 진행하면
 
0000000000000000000000000000010 가장 XOR의 큰 값을 얻을 수 있습니다.
 
0000000000000000000000000000101 XOR 0000000000000000000000000000010
 
 0000000000000000000000000000111 = 7

 

3. 탐색해서 얻은 최대값을 결과로 출력합니다.

 

탐색을 통해 최대값 : 7
 
7을 결과로 출력 합니다.
 
 
  • BufferedReader를 사용하여 입력되는 정보를 저장합니다.
  • StringTokenizer를 이용하여 N개의 수를 띄어쓰기 기준 저장합니다.
  • N개의 수를 저장할 Trie을 생성합니다.
  • 첫 번째 수와 Root Node을 생성한 뒤 2 ~ N번째 수에 대해서 Trie에 저장 및 XOR 비교를 진행합니다.
  • 비교가 끝난 뒤 최대값을 BufferedWriter 저장합니다.
  • BufferedWriter에 저장된 결과를 출력합니다.
  • Node 클래스는 Trie에 각 노드를 정의한 것으로써, 2개의 BitIndex를 가리키는 int[2] 배열을 가지고 있습니다.
  • Trie클래스는 자료구조 Trie을 구현한 것입니다.
  • Trieinit은 자식 노드를 생성하는 함수입니다.
  • TireinsertBit의 정수를 저장하는 Trie의 저장하는 함수입니다.
  • TriesearchXOR을 진행할 때 최대값이 될 수 있는 값을 구하는 함수입니다.

결과코드

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

//Trie에 대한 Node 값
class Node{
    int[] child;
    //Node 생성자
    Node(){
        //Bit는 0, 1이기 때문에 [2]로 배열 초기화
        child = new int[2];
        //자식 노드가 없으면 -1로 표시
        Arrays.fill(child, -1);
    }
}
//Trie 관한 클래스
class Trie{
    //노드에 정보 저장하는 List
    List<Node> nodes;
    Trie(){
        nodes = new ArrayList<>();
    }
    //노드 생성 및 Index 반환하는 함수
    int init(){
        Node newNode = new Node();
        nodes.add(newNode);
        return nodes.size() - 1;
    }
    //Bit의 수를 추가하는 함수
    void insert(int cur, int idx, int val){
        if(idx == -1){
            return;
        }
        //현재 비트의 값
        int c = (val & (1 << idx)) == 0 ? 0 : 1;
        //해당 자식 노드가 없을 때 노드 생성
        if(nodes.get(cur).child[c] == -1){
            int nxtIdx = init();
            nodes.get(cur).child[c] = nxtIdx;
        }
        //다음 Bit값으로 이동
        insert(nodes.get(cur).child[c], idx - 1, val);
    }
    //XOR 비교하는 함수
    int search(int cur, int idx, int val, int sum){
        if(idx == -1){
            return sum;
        }
        //0 → 1, 1 → 0으로 변환
        int c = 1 - ((val & (1 << idx)) == 0 ? 0 : 1);
        //해당 자식 노드가 없으면 다시 원상태로 변환
        if(nodes.get(cur).child[c] == -1){
            c = 1 - c;
        }
        //다음 Bit 값 비교
        return search(nodes.get(cur).child[c], idx - 1, val, sum + (c << idx));
    }
}
public class Main {
    public static void main(String[] args) throws IOException {
        //입력값 처리하는 BufferedReader
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //결과값 출력하는 BufferedWriter
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        int N = Integer.parseInt(br.readLine());
        StringTokenizer st = new StringTokenizer(br.readLine()," ");
        int ans = Integer.MIN_VALUE;
        //Tire 생성
        Trie trie = new Trie();
        //Root Node 생성
        trie.init();
        //첫 번째 수 Trie에 저장
        trie.insert(0, 30, Integer.parseInt(st.nextToken()));
        //2 ~ N번째 수 Trie 저장 및 XOR 비교
        for(int i=1;i<N;i++){
            int val = Integer.parseInt(st.nextToken());
            //현재 수 Trie 저장
            trie.insert(0, 30, val);
            //XOR비교를 통해 최대값 비교
            ans = Math.max(ans, trie.search(0, 30, val, 0) ^ val);
        }
        //최대값 BufferedWriter 저장
        bw.write(String.valueOf(ans));
        bw.flush();		//결과 출력
        bw.close();
        br.close();
    }
}

 

댓글