의외로 알아내기 어려운 2의 거듭제곱을 더하면 나타나는 특징
1. 문제
27514번: 1차원 2048 (acmicpc.net)
27514번: 1차원 2048
첫 줄에 흐즈로가 정의한 연산을 $0$번 이상 수행해 만들 수 있는 가장 큰 최댓값을 출력하세요. 문제의 답은 $2^{62}$보다 크지 않음이 보장됩니다.
www.acmicpc.net
2. 풀이
0이나 2의 거듭제곱으로 이루어진 수열이 있는데, 서로 같은 두 수를 찾으면 하나를 2배 해주고 다른 수는 0으로 바꿔준다
이 과정을 반복했을때, 남아있는 수열의 수 중 최댓값을 찾는 문제
처음에는 걍 수의 위치가 중요한 문제는 아니니.. 수열의 값 a와 그 개수를 value로 해서 dict[a] = value로 만들고
dict를 순회해서 value가 2개 이상 있으면 2배한 dict[2a] += 1 해주고, dict[0] += 1 해주는 식으로..
그러니까 수열을 counting해서 dict로 바꾼 다음, dict를 순회해서 count값이 2개 이상 있으면 2배 해줘서 새로운 key,value 만드는 식으로
반복문이 끝나면 dict를 순회해서 key값이 최대인게 정답이겠지
def simulation(A_dict):
while 1:
new = {0:0}
find = False
for key,value in A_dict.items():
if key != 0:
if value >= 2:
value -= 2
new[2*key] = new.get(2*key,0) + 1
new[0] += 1
find = True
if value > 0:
new[key] = new.get(key,0) + value
elif value == 1:
new[key] = new.get(key,0) + value
elif key == 0:
new[0] += value
if find:
A_dict = new
else:
break
answer = 0
for key in A_dict:
if answer < key:
answer = key
return answer
n = int(input())
A = list(map(int,input().split()))
A_dict = {}
for i in range(n):
A_dict[A[i]] = A_dict.get(A[i],0) + 1
print(simulation(A_dict))
정직하게 위와 같이 해도 되기는 하지만... 관찰을 한다면 간단하게 풀 수 있다
2의 거듭제곱인 두 수 $a_{i}$와 $a_{j}$가 서로 같다면, 하나를 $2a_{i}$라 하고 하나를 0으로 한다는데..
이게 무슨 의미인가?
$2^{k} + 2^{k} = 2^{k} (1+1) = 2 * 2^{k} = 2^{k+1}$이더라고
그러니까 알고보니 두 수가 서로 같으면 그냥 더하면 되는거더라고
1) 특히 중요한 점은, 서로 같은 2의 거듭제곱을 더한다는 것은 그냥 2배하는 것과 같다.
즉 두 수를 더한 걸 수열에 추가하고 하나는 0으로 바꿔주고
여기에 또 하나 핵심적인 관찰은 서로 다른 두 2의 거듭제곱을 더한다면?
2) 만약 a < b라면, $2^{b} < 2^{a} + 2^{b} < 2^{b+1}$이 성립하더라고
몇개 해보면 진짜 그러더라 2 < 1 + 2 < 4, 4< 1 + 2 + 4 < 8, 8< 1 + 2 + 4 + 8 < 16, 16 < 1 + 2 + 4 + 8 + 16 < 32 ....
그러므로, 두 사실을 이용해서, 2의 거듭제곱 또는 0으로 이루어진 주어진 수열의 모든 수를 전부 더한다면?
$a_{1} < a_{2} < ... < a_{k} < a_{k+1}$이라고 할때,
$$2^{a_{k+1}} <= 0 + 2^{a_{1}} + 2^{a_{2}} + .... + 2^{a_{k}} + 2^{a_{k+1}} < 2^{a_{k+1} + 1}$$이 성립한다는 사실을 알 수 있다.
여기서 찾고자 하는 수열의 최댓값은, $2^{a_{k+1}}$이다.
그러므로, 주어진 수열의 수를 전부 더한 $s = 0 + 2^{a_{1}} + ... + 2^{a_{k+1}}$을 구한다.
여기서 2가 밑인 로그를 취한다면...
$$a_{k+1} <= log2(s) < a_{k+1} + 1$$이다.
따라서, log2(s)에 int()함수를 취하면 $a_{k+1}$을 얻는다.
그러므로, $2^{log2(s)}$가 정답이 된다.
import math
n = int(input())
a = list(map(int,input().split()))
print(2**int(math.log2(sum(a))))
3. 문제2
27515번: 1차원 2048과 쿼리 (acmicpc.net)
27515번: 1차원 2048과 쿼리
$Q$개의 쿼리에 대해 각각 한 줄에 문제의 정답을 출력하세요. $a$가 빈 수열인 경우 문제의 정답은 $0$으로 간주합니다. 모든 쿼리에 대해 문제의 정답이 $2^{62}$보다 크지 않음이 보장됩니다.
www.acmicpc.net
4. 풀이
수열이 시작에는 빈 수열인데, 쿼리로 수를 추가하고 연산을 수행해서 최댓값을 찾고, 수를 빼고 연산을 수행해서 최댓값을 찾고 해야한다..
어쨌든 내가 최초로 한 counting dict로 바꿔서 푸는 방법은 적용할 수가 없더라..
하지만 2의 거듭제곱을 더하면 나타나는 특징을 이용한다면 쉽게 해결할 수 있다
여기서 중요한점은.. 처음에 s가 +2 +3 +4... 처럼 한자리만 있을테니 s[0], s[1]로만 구성되어있겠지 생각했는데..
생각해보니 +1024나 +2048도 들어올수 있는데 s[1]을 한다면 1024, 2048을 추가해야하는데 1,2가 추가되어서 틀리겠지
s[1:]를 int로 바꿔줘야겠다
이런 간단한것도 실수하다니... 기본을 튼튼하게 해야돼
import math
from sys import stdin
q = int(stdin.readline())
a = 0
for _ in range(q):
s = stdin.readline().rstrip()
if s[0] == '+':
a += int(s[1:])
else:
a -= int(s[1:])
if a == 0:
print(0)
else:
print(2**int(math.log2(a)))
'알고리즘 > 애드 혹 알고리즘' 카테고리의 다른 글
XOR 문제에 접근하기 위해 반드시 필요한 스킬1 - 기본 성질 - (0) | 2024.01.23 |
---|---|
ABC 334 복기 - 배열에서 원소 하나를 제거해서 두 원소 절댓값 차이의 합을 최소로 만들기(prefix sum, suffix sum) (0) | 2024.01.01 |
최소 길이의 실만 사용해서 구슬을 원형으로 배열하는 놀라운 방법 (0) | 2023.02.04 |
100번할 것을 1번만에 하는 나눗셈 연산 -시험 감독- (0) | 2022.11.03 |
매우 큰 수를 나머지 연산으로 줄일 수 있는 마법(백준-개미) (0) | 2022.08.30 |