백준 19847: 여우 신탁
https://www.acmicpc.net/problem/19847
19847번: 여우 신탁
“여우신님, 여우신님, 번호 하나만 내려 주세요…” 누구나 1 이상 45 이하의 정수 여섯 개를 잘 골라서 인생이 달라지거나, 혹은 1 이상 10,000 이하의 정수 하나를 잘 골라서 맞았습니다!!를 받는
www.acmicpc.net
예를 들어 일단 첫 여우는 9개의 수 중 하나를 달라고 할 것이다. 그러면 9개의 숫자들이 나올 확률은 전부 같다. 즉, 각 숫자가 뽑히는 경우의 수를 저장한 배열은
int p[]={1,1,1,1,1,1,1,1,1};
로 나타낼 수 있다.
STEP 0.
입력 두 번째 줄에 받는 숫자들이 예를 들어 9 4 2 라면 여우신은 4 이상 수들은 전부 4로 나눈 나머지를 계산해야 하고, 또 그 후 2 이상 수들은 전부 2로 나눈 나머지를 계산해야 한다.
하지만 예를들어 10 3 5라면 여우신은 3번째 숫자를 부를 땐 굳이 5로 나눈 나머지를 계산하지 않아도 된다. 그 전에 나온 수들이 무조건 3 미만이기 때문에 5로 나눈 나머지는 그 수 그대로이기 때문이다.
그러므로 우리는 두 번째 줄을 입력받을 때 감소하는 수열만 취하고 나머지는 버리면 된다.
ex) 9 4 2 -> 9 4 2 전부 취함
10 3 5 -> 10 3만 가져가고 5는 버림
11 9 15 3 1 -> 11 9 3 1만 가져가고 15는 버림
STEP 1.
9 4 2를 예로 들어 설명하겠다.
'9' 란 0~8까지 각 숫자가 뽑히는 경우의 수가 1임을 뜻한다.
'8'이란 0~7까지 각 숫자가 뽑히는 경우의 수가 1임을 뜻한다.
'n'이란 0~n-1까지 각 숫자가 뽑히는 경우의 수가 1임을 뜻한다.
'9'+'8'란 0~8까지 각 숫자가 뽑히는 경우의 수가 1이고, 거기에 추가로 0~7까지 각 숫자가 뽑히는 경우의 수에 1을 추가한다는 뜻이다.
즉, 0~7까지 각 숫자가 뽑히는 경우의 수는 2이고, 8이 뽑히는 경우의 수는 1이다.
처음에 9개의 수 중 하나를 뽑고 시작했으니 전체 경우의 수는 9이다.
이 때 상황은 '9'로 나타낼 수 있다.
그 다음, 여우신은 4로 나눈 나머지를 구한다.
0~8을
0 1 2 3 / 4 5 6 7 / 8
이렇게 구간으로 나눠보자.
0 1 2 3 은 4로 나누면 그대로 1 2 3 4이다.
4 5 6 7 은 4로 나누면 1 2 3 4가 된다.
8은 4로 나누면 0이 된다.
즉, '9'는 4로 나눈 나머지를 구하는 과정에서 '4'+'4'+'1'이 된다.
(바꾸기 전 '9' 개수)*(9를 4로 나눈 몫)=(바꾼 후 '4' 개수) 이다.
1 * 2 = 1
(바꾸기 전 '9' 개수) = (바꾼 후 '9를 4로 나눈 나머지' 개수) 이다.
(바꾸기 전 '9' 개수) = (바꾼 후 '1' 개수)
1 = 1
그 다음, 여우신은 2로 나눈 나머지를 구한다.
비슷한 과정을 거치면 '4'+'4'+'1' 은 '2'+'2' + '2'+'2' + '1'이 된다.
(바꾸기 전 '4' 개수)*(4를 2로 나눈 몫)=(바꾼 후 '2' 개수) 이다.
2 * 2 = 4
(바꾸기 전 '4' 개수) = (바꾼 후 '4를 2로 나눈 나머지' 개수) 이다.
(바꾸기 전 '4' 개수) = (바꾼 후 '0' 개수)
하지만 '0'의 개수를 따지는 것은 의미가 없으니 생략하자.
'i'의 개수를 dp[i]라고 하자.
아까 예시에선 dp[2]=4, dp[1]=1인 것이다.
처음 상태는 dp[9]=1으로 시작한다.
for(int i=9;i>=0;i--) 을 통해
9에서 시작해서 0으로 1씩 감소하는 i를 설정하자.
일단 9를 4로 나눈다. 그럼 몫은 2이고 나머지는 1이다.
나머지가 1이기 때문에 dp[1]에 dp[9]만큼 증가시킨다.
몫이 2이기 때문에 dp[4]에 2*dp[9]만큼 증가시킨다.
dp[8]~dp[5]는 전부 0이기 때문에 생략하고...
i가 점점 감소해 4에 도달했다.
그럼 이제부터는 여우신은 4가 아니라 2로 나누면 된다.
현재 상태는 dp[4]=2, dp[1]=1이고 그 외에는 전부 0이다.
4를 2로 나눈 몫은 2이고 나머지는 0이다.
그러므로 dp[0]에 dp[4]만큼 증가시킨다 ( 실제로 dp[0]값은 의미 없기때문에 생략 가능 )
몫이 2이기 때문에 dp[2]에 2*dp[4]만큼 증가시킨다.
STEP 2.
'2'+'2'+'2'+'2'+'1'같은 상태는
dp[1]=1, dp[2]=4처럼 저장된다.
우리는 평균을 구할 거기 때문에
sum=0으로 시작해서
각 i에 대해 i*(i-1)/2를 dp[i]번 sum에다 더해주면 된다.
그 후, sum을 전체 경우의 수, 즉 9로 나누면 된다. (맨 처음에 0~8 총 9개 수 중 하나를 뽑았으니)
여우신이 마지막 수를 뽑을 때 2로 나눈 나머지를 구했으므로 i=2까지만 계산해주자. dp[3]부터는 쓸데없는 값들이 저장되어 있다.
다음은 C++로 구현한 것이다.
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
|
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
int main()
{
int n;
scanf("%d", &n);
vector<int> v;
int b = 10000005;
//STEP 0
for (int p = 0;p < n;p++)
{
int h;
scanf("%d", &h);
if (h < b)
{
v.push_back(h);
b = h; // 3시간동안 삽질한 이유
}
}
int g = v.front();
int dp[1000006] = { 0 };
//STEP 1
dp[g] = 1;
int ind = 0;
for (int i = g;i >= 0;i--)
{
int j = v[ind];
if (i <= j)
{
ind++;
if (ind == v.size())
break;
}
j = v[ind];
dp[i % j] += dp[i];
dp[j] += (i / j) * dp[i];
}
ind--;
//STEP 2
ll sum = 0;
ll ggg = g;
for (ll i = 0;i < v[ind] + 1;i++)
{
ll ti = i * (i - 1) / 2;
ll ddd = dp[i];
sum += ddd * ti;
}
ll div = sum / ggg;
printf("%lld.", div);
ll c = sum;
for (ll i = 0;i < 13;i++) // 13은 소수점 아래 13번째 자리까지 출력하고 싶다는 의미
{
c = c % ggg;
c *= 10;
printf("%d", c / ggg);
}
}
|
cs |
사실 dp[] 배열에 단순히 dp[i]에 i가 뽑히는 경우의 수를 저장하고 풀어도 답이 나온다. 하지만 내 풀이를 소개하고 싶어 이렇게 썼다. 무려 3시간동안 무려 틀렸습니다 14개를 받으며 겨우 풀었는데 허무하게도 사실 틀린 이유는 바로 20번째 줄의 b=h;를 if문 바깥에 놓아서 그런 거였다.
dp[] 배열에 단순히 dp[i]에 i가 뽑히는 경우의 수를 저장하는 풀이는 다음과 같다.
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
|
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<vector>
using namespace std;
typedef long long ll;
int main()
{
int n;
scanf("%d", &n);
vector<int> v;
int b = 10000005;
for (int p = 0;p < n;p++)
{
int h;
scanf("%d", &h);
if (h < b)
{
b = h;
v.push_back(h);
}
}
int g = v.front();
int dp[1000006] = { 0 };
for (int i = 0;i < g;i++)
{
dp[i] = 1;
}
int ind = 1;
for (int i = g - 1;i >= 0;i--)
{
if (ind >= v.size())
break;
if (v[ind] > i)
ind++;
if (ind >= v.size())
break;
dp[i % v[ind]] += dp[i];
}
ind--;
ll sum = 0;
ll div = 0;
ll ggg = g;
for (ll i = 0;i < v[ind];i++)
{
ll ddd = dp[i];
sum += i * ddd;
}
div = sum / ggg;
printf("%lld.", div);
ll c = sum;
for (ll i = 0;i < 13;i++)
{
c = c % ggg;
c *= 10;
printf("%d", c / ggg);
}
}
|
cs |