Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

Typical DP Contest D - サイコロ

問題文 : サイコロ


解法 :

$dp[i][j][k] := 2^i3^j5^k$ が出来る確率として$N$ 回dp テーブルを回していく.
ここで, 2, 3, 5 以外の素因数が含まれていればその数が出来ないことに注意する.

コード :

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <vector>
#include <algorithm>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <list>
#include <set>
#include <map>

using namespace std;

double dp[2][70][70][70];

int main()
{
	int N;
	int count[6] = {0};
	long long int D;
	
	scanf("%d %lld", &N, &D);
	
	while (D % 2 == 0) {count[2]++; D /= 2;}
	while (D % 3 == 0) {count[3]++; D /= 3;}
	while (D % 5 == 0) {count[5]++; D /= 5;}
	
	if (D != 1) return (!printf("0\n"));
	
	for (int i = 0; i < 2; i++)
		for (int j = 0; j < 70; j++)
			for (int k = 0; k < 70; k++)
				for (int l = 0; l < 70; l++)
					dp[i][j][k][l] = 0;
	
	dp[0][0][0][0] = 1;
	for (int i = 1; i <= N; i++){
		for (int j = 0; j <= 67; j++) for (int k = 0; k <= 67; k++) for (int l = 0; l <= 67; l++){
			dp[i & 1][j][k][l] = 0;
		}
		
		for (int j = 0; j <= count[2]; j++) for (int k = 0; k <= count[3]; k++) for (int l = 0; l <= count[5]; l++){
			double p = 1 / 6.0;
			dp[i & 1][j][k][l] += p * dp[(i - 1) & 1][j][k][l];
			dp[i & 1][min(count[2], j + 1)][k][l] += p * dp[(i - 1) & 1][j][k][l];
			dp[i & 1][j][min(count[3], k + 1)][l] += p * dp[(i - 1) & 1][j][k][l];
			dp[i & 1][min(count[2], j + 2)][k][l] += p * dp[(i - 1) & 1][j][k][l];
			dp[i & 1][j][k][min(count[5], l + 1)] += p * dp[(i - 1) & 1][j][k][l];
			dp[i & 1][min(count[2], j + 1)][min(count[3], k + 1)][l] += p * dp[(i - 1) & 1][j][k][l];
		}
	}
	
	double ans = 0;
	for (int i = count[2]; i <= 67; i++) for (int j = count[3]; j <= 67; j++) for (int k = count[5]; k <= 67; k++){
		ans += dp[N & 1][i][j][k];
	}
	
	printf("%.9lf\n", ans);
	
	return (0);
}