반응형
https://www.acmicpc.net/problem/1176
1176번: 섞기
첫 줄에는 학생의 수 N(1 ≤ N ≤ 16)과 최소 넘어야할 키의 차이 값 K(1 ≤ K ≤ 3,400)가 주어진다. 다음 N개의 줄에는 학생들의 키가 순서대로 들어온다. 키는 25,000 이하인 자연수만 들어온다.
www.acmicpc.net
문제 요약
학생 $N$명이 줄을 서야 한다. 각 학생은 자신과 이웃한 모든 학생과 키의 차이가 $K$보다 크도록 줄을 서야 할 때, 줄을 서는 경우의 수를 구하는 문제이다.
문제 해결
$N$의 제한 $1 \le N \le 16$에 주목하자. bitmask dp가 딱 떠오르는 제한이다.
다음과 같이 식을 세워보자.
$dp[i][j] = $지금까지 줄을 선 학생들의 집합이 이진수 $j$로 표현되고, 그 중 가장 오른쪽 학생이 $i$인 경우의 수
이 dp식이 성립하려면, 서로 옆에 있을 수 있는 학생들을 알고있어야 하고, 이는 $O(N^2)$에 전처리할 수 있다.
$dp[i][j]$는 어떻게 구할까?
$j$에서 $i$를 세우지 않은 경우로 돌아가보자. 그 때 여전히 줄에 서있는 학생들의 집합은 $j - 2^i$라는 이진수로 표현 가능하다.
$j - 2^i$를 이진수로 나타냈을 때 켜져있는 비트(1)에 대해 그 학생이 가장 오른쪽에 선 경우를 모두 더해주면 된다.
코드
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll dp[16][65536]{};
bool vis[16][65536]{};
int edge[16][16]{};
int N, K;
ll down(int n, int k) {
if (vis[n][k]) return dp[n][k];
vis[n][k] = 1;
int g = k - (1 << n);
for (int i = 0; i < N; i++) {
if (!(g & (1 << i))) continue;
if (!edge[i][n]) continue;
dp[n][k] += down(i, g);
}
return dp[n][k];
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> N >> K;
int arr[16]{};
for (int i = 0; i < N; i++) {
dp[i][1 << i] = 1;
cin >> arr[i];
for (int j = 0; j < i; j++) {
if (abs(arr[i] - arr[j]) > K) edge[i][j] = edge[j][i] = 1;
}
}
ll ans = 0, dest = (1LL << N) - 1;
for (int i = 0; i < N; i++) ans += down(i, dest);
cout << ans;
}
bitmask dp의 입문용 문제로 느껴졌다. 만약 $N$의 제한이 더 작았더라면, bitmask dp임을 알아채는데 더 오래 걸렸을 것 같다는 생각이 들었다.
반응형
'문제 풀이' 카테고리의 다른 글
| 4월의 문제 풀이 - (2) (0) | 2024.04.22 |
|---|---|
| 4월의 문제 풀이 - (1) (2) | 2024.04.13 |
| BOJ 24502 - blobsad [C++] (0) | 2023.12.12 |
| BOJ 9015 - 정사각형 [C++] (0) | 2023.12.12 |
| BOJ 14461 - 소가 길을 건너간 이유 7 [C++] (0) | 2023.12.12 |