티스토리 뷰

알고리즘/백준

부분수열의 합 1182번 백준

글을 쓰는 개발자 2020. 12. 9. 22:02
반응형

문제:www.acmicpc.net/problem/1182

 

1182번: 부분수열의 합

첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.

www.acmicpc.net

이 문제는 기존의 방식으로 모든 가능한 수열의 경우를 다 확인하는 식으로 코딩했었다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import sys
N,S=map(int,sys.stdin.readline().rstrip().split())
li = list(map(int,sys.stdin.readline().split()))
passing=[False]*(N+1)
ans=[]
cnt=0
def func(ans,celi,aim,sum):
    global cnt,S
    if celi==aim:
        return
    for i in range(N):
        if not ans:
            passing[i]=True
            ans.append(i)
            sum+=li[i]
            if li[i]==S:
                cnt+=1
            func(ans,celi+1,aim,sum)
            sum-=li[i]
            ans.pop()
            passing[i]=False
        else:
            if not passing[i] and ans[-1]<i:
                passing[i]=True
                ans.append(i)
                sum+=li[i]
                if sum==S:
                    cnt+=1
                func(ans,celi+1,aim,sum)
                sum-=li[i]
                ans.pop()
                passing[i]=False
func(ans,0,N,0)
print(cnt)
cs

처음에는 ans에 값이 없으므로 if not ans: 를 통해 값을 집어 넣고 다 더한 값이 S가 되는지 확인한다.

그리고 ans에 값이 존재한다면 그 뒤 나머지 수열을 생성하면서 S가 가능한지 확인하고 갯수를 갱신한다.

하지만 이 코드는 시간면에서 다소 아쉬운 코드였다. 

그래서 다른 분들의 코드를 봤다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import sys
input = sys.stdin.readline
def dfs(idx, sum):
    global cnt
    if idx >= n:
        return
    sum += s_[idx]
    if sum == s:
        cnt += 1
    dfs(idx + 1, sum - s_[idx])
    dfs(idx + 1, sum)
n, s = map(int, input().split())
s_ = list(map(int, input().split()))
cnt = 0
dfs(00)
print(cnt)
cs

참고:https://pacific-ocean.tistory.com/306

 

[백준] 1182번(python 파이썬)

문제 링크: https://www.acmicpc.net/problem/1182 1182번: 부분수열의 합 첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에..

pacific-ocean.tistory.com

코드의 전체적인 흐름은 비슷하다. 하지만 이제 재귀부분에서 나의 코딩의 경우 초기값의 유무에 따른 if,else와 현재 그 값을 지났는지 안 지났는지 확인하는 부분에서 시간에 대한 소모가 많이 일어났을 것 같다.

순전히 각 원소가 포함되는 경우 포함되지 않는 경우 2가지의 경우를 각각 따져보면 되므로 

 

1
2
3
dfs(idx + 1, sum - s_[idx])
dfs(idx + 1, sum)
 
cs

이렇게 확인 하면 된다.

비트 마스크 관점으로도 보자!

 

1
2
3
4
5
6
7
8
9
10
11
N,S = map(int,input().split())
ar = list(map(int,input().split()))
ans=0
for i in range(1,1<<N):
    sum=0
    for k in range(N):
        if i&(1<<k):
            sum+=ar[k]
    if sum==S: ans+=1
 
print(ans)
cs

첫 번째 for문의 의미는 다음과 같다.

 

1
2
5 0
-7 -3 -2 5 8
cs

위의 예시는 백준에 있는 예제이다.

만일 5개의 원소가 있으면 총 가능한 수열의 갯수는 31개이다. 이때 이를 비트(2진수)로 표현하면

00000,00001,00010,00011,...,11111까지로 해당 수열의 존재 유무를 표현 할 수가 있다.

그리고 안의 for문은 해당 위치의 값이 존재하면 sum에 더하라 라는 의미를 가진다.그리고 밑 if문에 sum과 S가 같을 경우에는 갯수를 증가시킨다.
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함