https://www.acmicpc.net/problem/17142

 

17142번: 연구소 3

인체에 치명적인 바이러스를 연구하던 연구소에 승원이가 침입했고, 바이러스를 유출하려고 한다. 바이러스는 활성 상태와 비활성 상태가 있다. 가장 처음에 모든 바이러스는 비활성 상태이고, 활성 상태인 바이러스는 상하좌우로 인접한 모든 빈 칸으로 동시에 복제되며, 1초가 걸린다. 승원이는 연구소의 바이러스 M개를 활성 상태로 변경하려고 한다. 연구소는 크기가 N×N인 정사각형으로 나타낼 수 있으며, 정사각형은 1×1 크기의 정사각형으로 나누어져 있다. 연구소는

www.acmicpc.net

주어진 배열에 있는 바이러스들 중 m개를 골라 활성화시켜서 연구소에 퍼뜨리는 문제이다.

m개를 고르는 모든 경우를 해보고, 골랐을 때마다 bfs로 바이러스를 퍼뜨려서

모두 퍼뜨리는데 걸리는 최소시간을 구하면 된다. 즉 완전 탐색 + bfs 문제이다.

 

이 문제에서 주의 할점은 처음에 활성화하지 않은 바이러스들이다.

이 바이러스들은 애초에 퍼져있는 상태나 마찬가지이므로 이 위치는 0초에 퍼져있는 상태나 마찬가지이다.

그래서 처음에 시간을 0으로 처리해줬다가 bfs부분에서 넘어가 버려서 다음과 같은 테스트 케이스에서 -1이 나왔다.

(원래 정답은 2가 나와야 함)

 

4 2

0110

2112

2112

0110

 

활성화하지 않은 바이러스를 벽처럼 여겨서 넘어가지 못했기 때문이다.

그래서 일단 얘네들도 빈칸처럼 똑같이 처리해줬다.

그리고 마지막에 모두 퍼뜨리는데 걸리는 시간을 구할 때 좌표의 값이 2라면(바이러스였다면) 최대 시간으로 간주하지 않는다.

밑의 코드에서는 0인 경우(원래 빈칸이었던 경우)에만 최대 시간을 계산해주었다.

 

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
 
int n, m;
int map[50][50];
int time[50][50];
int ans;
vector<pair<intint>> virus;
vector<pair<intint>> alive;
 
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
 
int check() {
    int maxtime = 0;
 
    //모든 칸을 검사
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
 
            //처음에 활성화하지 않은 바이러스의 시간을 최대값으로 저장하면 안되므로
            //현재 칸이 원래 빈칸이었고(바이러스가 아니고)
            // 최대시간보다 더 긴 시간을 가지고 있으면 최대시간으로 저장
            if (map[i][j] == 0 && maxtime < time[i][j]) {
                maxtime = time[i][j];
            }
 
            //빈칸인데 시간이 저장되어 있지 않다(바이러스가 퍼지지 못했다)
            if (map[i][j] == 0 && time[i][j] == -1) {
                return -1;
            }
        }
    }
 
    //모두 걸리는데 걸리는 시간을 리턴
    return maxtime;
}
 
void bfs() {
    memset(time, -1sizeof(time));
    queue<pair<intint>> q;
    int x, y;
 
 
    //활성 바이러스들을 큐에 넣어준다.
    for (int i = 0; i < m; i++) {
        x = alive.at(i).first;
        y = alive.at(i).second;
        q.push(make_pair(x, y));
        time[x][y] = 0;
    }
 
 
    int nx, ny;
    while (!q.empty()) {
        x = q.front().first;
        y = q.front().second;
        q.pop();
 
        //인접한 네방향으로 퍼진다.
        for (int k = 0; k < 4; k++) {
            nx = x + dx[k];
            ny = y + dy[k];
 
            //범위 체크
            if (nx < 0 || ny < 0 || nx >= n || ny >= n) continue;
 
            //벽인 경우
            if (map[nx][ny] == 1continue;
 
            //빈 칸에 이미 방문
            if (time[nx][ny] != -1continue;
 
            q.push(make_pair(nx, ny));
            time[nx][ny] = time[x][y] + 1;
        }
 
    }
 
    //check함수로 모두 퍼질 수 없는 경우인 경우 -1, 그렇지 않은 경우 모두 퍼지는데
    //걸리는 시간을 받아온다.
    int tmp = check();
 
    if (tmp == -1) {
        //초기값이라면 -1을 그대로 넣어준다.
        if (ans == 10000000) {
            ans = -1;
        }
    }
    else {
        //받아온 시간이 최솟값이라면 ans에 저장
        //ans가 -1인 경우에도 새로 받아온 값(가능한 경우)로 바꿔줄 수 있다.
        if (ans == -1 || ans > tmp) {
            ans = tmp;
        }
    }
 
 
}
 
 
//활성화 시킬 바이러스 m개를 고른다.
void select(int index, int cnt) {
 
    //m개를 고른 경우
    if (cnt == m) {
        //바이러스를 활성화 시킨다.
        bfs();
        return;
    }
 
    //고를 수 있는 경우를 모두 넘어간 경우 리턴
    if (index >= virus.size()) return;
 
 
    //index번째 바이러스의 좌표를 가져온다.
    int x = virus.at(index).first;
    int y = virus.at(index).second;
 
 
    //index번째 바이러스를 선택한다.
    alive.push_back(make_pair(x, y));
    select(index + 1, cnt + 1);
 
 
    //index번째 바이러스를 선택하지 않는 경우
    alive.pop_back(); //선택하는 경우에서 넣은 거 뺀다
    select(index + 1, cnt);
 
}
 
 
 
int main() {
    cin >> n >> m;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> map[i][j];
 
            //바이러스인 경우 벡터에 좌표를 저장
            if (map[i][j] == 2) virus.push_back(make_pair(i, j));
        }
    }
 
    //최솟값을 찾기 위해서 큰 값을 넣어둔다.
    ans = 10000000;
    select(00);
 
    cout << ans << '\n';
 
    //선택하지 않은 바이러스 부분에도 시간 넣어줘야한다!!!!
 
 
    return 0;
}
Colored by Color Scripter
 
 
 

 

 

'BOJ' 카테고리의 다른 글

[BOJ] 17144. 미세먼지 안녕!  (0) 2019.07.30
[BOJ] 17140. 이차원 배열과 연산  (0) 2019.07.30
[BOJ] 1806. 부분합  (0) 2019.07.16
[BOJ] 2003. 수들의 합 2  (0) 2019.07.15
[BOJ] 2748. 피보나치 수 2  (0) 2019.07.15

https://www.acmicpc.net/problem/1806

 

1806번: 부분합

문제 10,000 이하의 자연수로 이루어진 길이 N짜리 수열이 주어진다. 이 수열에서 연속된 수들의 부분합 중에 그 합이 S 이상이 되는 것 중, 가장 짧은 것의 길이를 구하는 프로그램을 작성하시오. 입력 첫째 줄에 N (10 ≤ N < 100,000)과 S (0 < S ≤ 100,000,000)가 주어진다. 둘째 줄에는 수열이 주어진다. 수열의 각 원소는 공백으로 구분되어져 있으며, 10,000이하의 자연수이다. 출력 첫째 줄에 구하고자 하는 최소의 길

www.acmicpc.net

수열에서 연속된 수들의 부분합 중에 그 합이 S 이상이 되는 것 중, 가장 짧은 것의 길이를 구하는 문제이다.

 

left와 right 변수 두 개를 포인터로 놓고 구해줬다.

left는 부분 수열의 시작 부분, right는 부분 수열의 마지막 부분을 가리킨다.

 

가장 짧은 것의 길이를 구하기 위해 최대 길이인 n+1을 ans에 저장해놓는다.

그럼 이제 부분합을 s와 비교해가며 구해주면 된다.

 

먼저 합이 s보다 작은 경우에는 right를 증가시켜서 합에 수열의 원소를 더해준다.

합이 s보다 클 경우에는 left가 가리키는 원소를 합에서 빼준 후에 left값을 뒤로 옮긴다.

이때, s보다 큰 경우도 정답에 포함이므로 그때의 부분 수열의 길이(right-left+1)를 구해준다.

마찬가지로 합이 s가 되었을 때도 부분 수열의 길이를 구해주면 된다.

 

이 과정을 left가 right보다 커져버리거나 right가 범위를 벗어나는 경우가 될 때까지 반복한다.

 

합이 s이상이 되는 경우가 없다면 처음에 저장해 놓은 n+1 값이 ans에 저장되어 있으므로

ans가 그대로 n+1 인 경우에는 문제 조건에 따라 0을 출력한다.

 

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
using namespace std;
 
int n, s;
int num[100000];
 
int main()
{
 
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> n >> s;
 
    for (int i = 0; i < n; i++) {
        cin >> num[i];
    }
    
    int ans = n+1;
    
    int sum = num[0];
    int left = 0;
    int right = 0;
 
    while(left <= right && right < n) {
        
        if (sum < s) {
            right++;
            sum += num[right];
        }
        else if (sum > s) {
            int len = right - left + 1;
            if (ans > len) ans = len;
 
 
            sum -= num[left];
            left++;
        }
        else {
            int len = right - left + 1;
            if (ans > len) ans = len;
 
 
            right++;
            sum += num[right];
        }
 
    }
 
    if (ans == n + 1) ans = 0;
    cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 17140. 이차원 배열과 연산  (0) 2019.07.30
[BOJ] 17142. 연구소 3  (0) 2019.07.30
[BOJ] 2003. 수들의 합 2  (0) 2019.07.15
[BOJ] 2748. 피보나치 수 2  (0) 2019.07.15
[BOJ] 14889. 스타트와 링크(비트마스크 풀이)  (0) 2019.07.11

https://www.acmicpc.net/problem/2003

 

2003번: 수들의 합 2

첫째 줄에 N(1≤N≤10,000), M(1≤M≤300,000,000)이 주어진다. 다음 줄에는 A[1], A[2], …, A[N]이 공백으로 분리되어 주어진다. 각각의 A[x]는 30,000을 넘지 않는 자연수이다.

www.acmicpc.net

수열의 i번째 수부터 j번째 수까지의 합 A[i]+A[i+1]+…+A[j-1]+A[j]가 M이 되는 경우의 수를 구하는 문제이다.

n범위가 크지 않아서 단순히 반복문으로 모든 경우를 구해줬다.

 

0번째부터 n-1번째까지 각 자리에서 부터 시작하는 모든 경우를 해준다.

각 자리에서 시작할 때마다 합을 구해준다. 합이 m을 넘어가 버리면 해당 경우는 더 구해줄 필요가 없다.

(수열의 원소들은 자연수이므로 계속 더해줘봤자 m을 넘어가기 때문)

 

합이 m이 되는 경우에는 경우의 수를 저장할 ans값을 증가시키고 break해준다(다음 자리에서 다시 시작)

 

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
35
36
37
38
39
40
#include <iostream>
using namespace std;
 
int n, m;
int num[10000];
 
int main()
{
 
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> n >> m;
 
    for (int i = 0; i < n; i++) {
        cin >> num[i];
    }
    
    int ans = 0;
    
 
    for (int i = 0; i < n; i++) {
        
        int sum = 0;
        for (int j = i; j < n; j++) {
            sum += num[j];
            if(sum > m) break;
            if (sum == m) {
                ans++;
                break;
            }
        }
    }
 
 
    cout << ans << '\n';
 
 
    return 0;
}
Colored by Color Scripter
 

 

'BOJ' 카테고리의 다른 글

[BOJ] 17142. 연구소 3  (0) 2019.07.30
[BOJ] 1806. 부분합  (0) 2019.07.16
[BOJ] 2748. 피보나치 수 2  (0) 2019.07.15
[BOJ] 14889. 스타트와 링크(비트마스크 풀이)  (0) 2019.07.11
[BOJ] 14391. 종이 조각  (0) 2019.07.11

https://www.acmicpc.net/problem/2748

 

2748번: 피보나치 수 2

문제 피보나치 수는 0과 1로 시작한다. 0번째 피보나치 수는 0이고, 1번째 피보나치 수는 1이다. 그 다음 2번째 부터는 바로 앞 두 피보나치 수의 합이 된다. 이를 식으로 써보면 Fn = Fn-1 + Fn-2 (n>=2)가 된다. n=17일때 까지 피보나치 수를 써보면 다음과 같다. 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597 n이 주어졌을 때, n번째 피보나치 수를

www.acmicpc.net

n번째 피보나치 수를 배열에 저장해놓는다.

그러면 n번째까지의 피보나치 수를 구하는데 O(n)이 걸리고 그 이후로 접근할 때는 O(1)이면 바로 접근 가능하다.

 

i번째 피보나치 수 = i-1번째 피보나치 수 + i-2번째 피보나치 수

0번째와 1번째를 미리 넣어놓고 이 점화식을 이용하면 모든 피보나치수를 구할 수 있다.

 

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
#include <iostream>
using namespace std;
 
long long pibo[91];
 
int main()
{
 
    int n;
    cin >> n;
 
    pibo[0= 0;
    pibo[1= 1;
 
    for (int i = 2; i <= 90; i++) {
        pibo[i] = pibo[i - 1+ pibo[i - 2];
    }
 
    cout << pibo[n];
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 1806. 부분합  (0) 2019.07.16
[BOJ] 2003. 수들의 합 2  (0) 2019.07.15
[BOJ] 14889. 스타트와 링크(비트마스크 풀이)  (0) 2019.07.11
[BOJ] 14391. 종이 조각  (0) 2019.07.11
[BOJ] 1182. 부분수열의 합  (0) 2019.07.11

https://www.acmicpc.net/problem/14889

 

14889번: 스타트와 링크

예제 2의 경우에 (1, 3, 6), (2, 4, 5)로 팀을 나누면 되고, 예제 3의 경우에는 (1, 2, 4, 5), (3, 6, 7, 8)로 팀을 나누면 된다.

www.acmicpc.net

앞에서 dfs로 풀었던 문제이지만 비트 마스크를 연습할 겸 다시 풀어보았다.

0부터 (1<<n)-1 까지의 모든 경우를 구하고 (1 << k)를 이용해서 각 자리의 수가 0이면 해당 자리는 1번 팀에 1이면 2번 팀에 넣어주도록 했다.

000111처럼 정확히 n/2로 0과 1이 나눠진 경우에만 능력치의 합을 계산해준다.

 

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <vector>
using namespace std;
 
int n;
int s[20][20];
vector<int> t1;
vector<int> t2;
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> n;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> s[i][j];
        }
    }
 
    int ans = -1;
    for (int i = 0; i < (1<<n); i++) {
        
        t1.clear();
        t2.clear();
 
 
        //0인 경우 t1에 그렇지 않은 경우 t1에 넣는다.
        for (int k = 0; k < n; k++) {
            if ((i & (1 << k)) == 0) {
                t1.push_back(k);
            }
            else {
                t2.push_back(k);
            }
        }
 
 
        //팀이 반으로 나눠지지 않은 경우 skip
        if (t1.size() > n / 2continue;
        if (t2.size() > n / 2continue;
 
 
        int t1sum = 0;
        int t2sum = 0;
        for (int i = 0; i < n / 2; i++) {
            for (int j = 0; j < n / 2; j++) {
                if (i == j) continue;
                t1sum += s[t1[i]][t1[j]];
                t2sum += s[t2[i]][t2[j]];
            }
        }
 
 
        int diff = t1sum - t2sum;
        if (diff < 0) diff = -diff;
        if (ans == -1 || ans > diff) ans = diff;
 
    }
 
    cout << ans << '\n';
 
    return 0;
}
 
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 2003. 수들의 합 2  (0) 2019.07.15
[BOJ] 2748. 피보나치 수 2  (0) 2019.07.15
[BOJ] 14391. 종이 조각  (0) 2019.07.11
[BOJ] 1182. 부분수열의 합  (0) 2019.07.11
[BOJ] 11723. 집합  (0) 2019.07.11

https://www.acmicpc.net/problem/14391

 

14391번: 종이 조각

영선이는 숫자가 쓰여 있는 직사각형 종이를 가지고 있다. 종이는 1×1 크기의 정사각형 칸으로 나누어져 있고, 숫자는 각 칸에 하나씩 쓰여 있다. 행은 위에서부터 아래까지 번호가 매겨져 있고, 열은 왼쪽부터 오른쪽까지 번호가 매겨져 있다. 영선이는 직사각형을 겹치지 않는 조각으로 자르려고 한다. 각 조각은 크기가 세로나 가로 크기가 1인 직사각형 모양이다. 길이가 N인 조각은 N자리 수로 나타낼 수 있다. 가로 조각은 왼쪽부터 오른쪽까지 수를 이어 붙인

www.acmicpc.net

모든 칸(숫자)에 대해서 가로로 자를 경우를 1, 세로로 자를 경우를 0으로 놓고 비트 마스크를 사용하면 모든 경우를 빠르게 계산해줄 수 있다.

 

먼저 모든 경우를 위해 반복문을 0부터 1 << (n*m) 까지로 해준다.

 

n=2 , m=3

123

312

 

위와 같은 예를 들면,

000000부터 111111까지의 모든 경우를 구하는데, 각 행을 한 줄로 합쳐줘서 123312로 보고 계산하면 된다.

001100이라고 치면 3,3만 세로로 자르고 나머지는 가로로 자르는 경우이다.

각 경우마다 합을 구해서 최댓값과 비교해주면 된다.

 

 

각 경우에서는 가로 종이조각과 세로 종이조각의 수들을 구하여 합을 구해주면 된다.

먼저 한 줄로 보고 계산해줘야 하므로 k = i*m +j로 설정했다.

위의 예제에서 1행 2열의 '1'의 index는 k=4가 된다.

 

 

이런 식으로 1 << k와 모든 경우의 수를 해줄 b를 & 연산하여 각 k번째 자리에 1이 있는지 1이 없는지 구해주면 된다.

자리에 1이 있는 경우 세로이고 0이 있는 경우는 가로이다.

 

 

가로인 애들을 먼저 구해주면

각 행에서만 붙어 있는 숫자만이 하나의 숫자가 될 수 있으므로 한 행이 끝나면 sum에 더해주고 새로 숫자를 구해준다.

같은 행에서 숫자를 합치던 중에 세로인 애가 나와도 sum에 더해주면 된다. 그리고 현재 숫자를 나타내는 변수는 0으로 해주고 새로 만들어야 한다.

숫자를 합치는 방법은 (이전 숫자*10 + 현재 숫자)를 해주면 된다.

예를 들어 기존에 2였다면 2*10+3을 해줘서 23을 만들 수 있다.

 

 

세로인 애들도 마찬가지로 구해주면 된다. 대신 한 열의 행을 먼저 봐서 세로로 숫자를 만들어 주도록 하면 된다.

세로는 해당 자리에 1이 있는 경우로 &연산 결과가 (001000) 이런 식으로 나올 것이다. 즉 0이 아닌 값이면 세로 숫자에 더해준다. 가로인 경우와 비슷하게 세로에서는 한 열이 끝나면 sum에 더해주고 새로 숫자를 만들어줘야 한다. 열 중간에 가로가 나오는 경우도 마찬가지이다.

 

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <iostream>
#include <string>
using namespace std;
 
int n, m;
int paper[4][4];
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> n >> m;
    string s;
 
    for (int i = 0; i < n; i++) {
        cin >> s;
        for (int j = 0; j < m; j++) {
            paper[i][j] = s[j] - '0';
        }
    }
 
    int ans = 0;
 
    //모든 경우 구한다.
    //0인 곳은 가로, 1인 곳은 세로
    for (int b = 0; b < (1 << (n*m)); b++) {
        int sum = 0;
 
 
        //가로모양으로 자른 애들 계산
        for (int i = 0; i < n; i++) {
            
            int now = 0;
            for (int j = 0; j < m; j++) {
                
 
                //정사각형 종이 조각을 한줄로 이어 붙였을 때의 인덱스
                int k = i*+ j;
 
                //k번째 자리값이 0이다(가로로 자를거다)                
                if ((b & (1 << k)) == 0) {
                    now = now * 10 + paper[i][j];
                }
                else { //1인 경우(세로인 경우) 앞에까지 만들어진 조각을 sum에 더해준다.
                    sum += now;
                    now = 0;
                }
            }
 
            //한 행이 끝났으면 만들어진 숫자 sum에 더해준다.
            sum += now;
        }
 
 
 
        //세로 모양으로 자른 애들 계산
        for (int j = 0; j < m; j++) {
 
            int now = 0;
            for (int i = 0; i < n; i++) {
 
                //정사각형 종이 조각을 한줄로 이어 붙였을 때의 인덱스
                int k = i*+ j;
 
                //세로인 경우에 숫자를 만들어준다.
                if ((b & (1 << k)) != 0) {
                    now = now * 10 + paper[i][j];
                }
                else {
                    sum += now;
                    now = 0;
                }
            }
 
            sum += now;
        }
        
 
        //최댓값과 비교
        if (ans < sum) ans = sum;
 
    }
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 2748. 피보나치 수 2  (0) 2019.07.15
[BOJ] 14889. 스타트와 링크(비트마스크 풀이)  (0) 2019.07.11
[BOJ] 1182. 부분수열의 합  (0) 2019.07.11
[BOJ] 11723. 집합  (0) 2019.07.11
[BOJ] 1987. 알파벳  (0) 2019.07.11

https://www.acmicpc.net/problem/1182

 

1182번: 부분수열의 합

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

www.acmicpc.net

비트마스크를 이용해서 모든 경우를 구해볼 수 있다.

 

문제의 예제 테스트 케이스를 이용해보면

5 0

-7 -3 -2 5 8

예제의 입력이 위와 같다.

 

 

 index : 4   3   2  1  0

 원소  : -7  -3  -2  5  8

00000 부터 11111( (1 << n) -1)까지의 경우를 모두 해주는데 각 자리의 숫자는 선택할 원소의 index라고 볼 수 있다.

예를 들어, 00101의 경우는 -2와 8을 선택한 경우라고 할 수 있다.

 

 

1을 포함하고 있는 자리(해당 자리의 원소를 선하는 경우)를 확인하기 위해서는 &연산을 사용하면 된다.

 

위의 00101과 1 << 2  (100)를 &연산을 해주면 00100 이 나와서 0이 아닌값(4)가 나오므로 2번째 자리에 1이 있는 것을 확인할 수 있고 마찬가지로 1 << 0 을해서 0번째자리에 1이 있는 것을 확인할 수 있다.

 

반대로 00101과 1<<3 (1000)을 &연산을 해주면 00000 이 나와서 0이 되므로 3번째 자리에 1이 없다는 것을 확인할 수 있따. 

 

이런식으로 각 자리에 1이 있으면 그 자리의 원소값을 sum에 더하고 

각자리의 확인을 마친뒤에는 sum이 s와 같은지 검사해주면된다.

 

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
35
36
37
38
39
#include <iostream>
using namespace std;
 
 
int n, s, ans;
int num[20];
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> n >> s;
 
    for (int i = 0; i < n; i++) {
        cin >> num[i];
    }
 
 
    for (int i = 1; i < (1 << n); i++) {
 
        int sum = 0;
        for (int j = 0; j < n; j++) {
            //j번째 자리수가 1을 가지고 있다.(수열의 j번째 원소를 선택하는 경우)
            if ((i & (1 << j)) != 0) {
                sum += num[j];
            }
        }
 
 
        //다 더한 값이 s
        if (sum == s) {
            ans++;
        }
    }
 
    cout << ans << '\n';
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 14889. 스타트와 링크(비트마스크 풀이)  (0) 2019.07.11
[BOJ] 14391. 종이 조각  (0) 2019.07.11
[BOJ] 11723. 집합  (0) 2019.07.11
[BOJ] 1987. 알파벳  (0) 2019.07.11
[BOJ] 7562. 나이트의 이동  (0) 2019.07.09

https://www.acmicpc.net/problem/11723

 

11723번: 집합

첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다. 둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <string>
using namespace std;
 
int m;
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
 
    cin >> m;
 
    int n = 20;
    int s = 0;
    int x;
    string command;
 
    while (m-- > 0) {
        
        
        cin >> command;
        //연산이 all이나 empty이 아닐때만 x를 입력받는다.
        if (command != "all" && command != "empty") {
            cin >> x;
            x--;
        }
        
 
        if (command == "add") {
            s = s | (1 << x);
        }
        else if (command == "remove") {
            s = s & ~(1 << x);
        }
        else if (command == "check") {
            if ((s & (1 << x)) == 0) {
                cout << 0 << '\n';
            }
            else {
                cout << 1 << '\n';
            }
        }
        else if (command == "toggle") {
            s = s ^ (1 << x);
        }
        else if (command == "all") {
            s = (1 << n ) - 1;
        }
        else if (command == "empty") {
 
            s = 0;
            
        }
        
    }
 
    return 0;
}
Colored by Color Scripter
 

'BOJ' 카테고리의 다른 글

[BOJ] 14391. 종이 조각  (0) 2019.07.11
[BOJ] 1182. 부분수열의 합  (0) 2019.07.11
[BOJ] 1987. 알파벳  (0) 2019.07.11
[BOJ] 7562. 나이트의 이동  (0) 2019.07.09
[BOJ] 2638. 치즈  (0) 2019.07.08

+ Recent posts