2023. 10. 11. 04:25ㆍAlgorithm & PS/문제 풀이
[백준] https://www.acmicpc.net/problem/23630
23630번: 가장 긴 부분 수열 구하기
$N$개의 자연수로 이루어진 수열 $A = \{A_1, A_2, …, A_N\}$가 주어진다. 다음의 조건을 모두 만족하는 $A$의 부분 수열 $\{A_{i_1}, A_{i_2}, ..., A_{i_m}\}$ 중 가장 긴 수열의 길이를 출력하라. $A_{i_1} ~\&~ A_{i
www.acmicpc.net
일단 문제의 조건은 문제 자체에도 적혀있지만 풀어서 적으면 아래와 같습니다.
1. 부분 수열의 원소들을 전부 bitwise AND를 했을 때 0이 되면 안됨
2. 부분 수열은 원래 수열에서 위치를 바꾸지 않고 유지해야 합니다. 또한, 동일한 위치의 원소를 여러번 사용하는 것도 불가능
3. 가능한 제일 긴 부분 수열의 길이를 출력
그렇다면 일단 bitwise AND를 했을 때 0이 안 되는 경우가 어떤건지를 생각해봅시다. 먼저, AND 연산은 두 비트가 1일 때 1, 아니면 0이 됩니다. 그리고 bitwise AND 결과가 0이 아니라면 어느 비트는 1이라는 거겠죠. 결과에서 어느 비트가 1이라면 해당 부분수열의 원소들의 해당 비트가 모두 1이어야 합니다.
이를 알아챘다면, 각 비트에 대해서 켜져있는 값들만 가지고 부분수열을 만들면 bitwise AND 결과가 0이 아닌 걸 알 수 있습니다. 그래서 모든 비트들에 대해서 켜져있는 값들의 개수를 구하고 그 중에서 제일 많은 개수를 정답으로 출력하면 됩니다.
코드는 아래와 같습니다. 시간복잡도는 \(O(N\log{1000000})\) 입니다. 다만 \(\log{1000000}\)은 \(30\)보다 작으므로 그냥 상수처럼 보셔도 됩니다.
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
ll N, M;
cin >> N;
vector<ll> a(N);
for(int i =0; i<N; ++i){
cin >> a[i];
}
ll ans = 0;
for(ll i =0; i<30; ++i){
ll val = ((ll)1 << i);
ll tmp = 0;
for(int i =0; i<N; ++i){
if(val&a[i]){
++tmp;
} // 해당 비트가 켜졌을 시
}
ans = max(ans, tmp);
} // 2^30 > 1'000'000
cout << ans;
return 0;
} // O(NlogN)
이를 보고 저는 처음에 문제의 조건을 잘못 이해했어서 bitwise AND를 했을 때 0인 아닌 LIS를 찾아야 하는건가 싶었습니다. 그래서 \(O(N\log{N})\) LIS를 활용해서 풀었었는데 당연히 틀렸습니다. 조건을 제대로 읽어야 한다는 교훈을 주는 문제였습니다.
'Algorithm & PS > 문제 풀이' 카테고리의 다른 글
[백준] BOJ 14727 : 퍼즐 자르기 (0) | 2023.10.15 |
---|---|
[백준] BOJ 27973 : 지연 평가 (0) | 2023.10.13 |
[백준] BOJ 3997 : 하이퍼드롬 (0) | 2023.10.11 |
[백준] BOJ 1655 : 가운데를 말해요 (0) | 2023.10.08 |
[백준] BOJ 22345 : 누적 거리 (0) | 2023.10.04 |