题意: 求用 N 个色子投出点数和为 M 的概率。
思路: 一开始用 dfs 交了一个答案,得了六十分。观察 dfs 的过程我们能看到有许多重复计算,也能看到很多子问题,比如求N==3,M==11,会利用到N==2,M==10的结果,所以可以采用记忆化搜索,或者直接就是DP做。
代码:
记忆化搜索:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<stack>
#include<queue>
#include<utility>
#include<vector>
#include<cmath>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#include<sstream>
using namespace std;
typedef long long LL;
int N, M;
double f[110][610];
double Dfs(int n, int m)
{
if(f[n][m] != 0) return f[n][m];
if(n == 1) return f[n][m] = 1.0/6;
for(int i=1; i<=6&&m-i>=n-1; i++){
if(m-i > (n-1)*6) continue;
f[n][m] += 1.0/6*Dfs(n-1, m-i);
}
return f[n][m];
}
int main()
{
while(scanf("%d%d", &N, &M) == 2){
printf("%.2lf\n", 100*Dfs(N, M));
}
return 0;
}
动态规划:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<stack>
#include<queue>
#include<utility>
#include<vector>
#include<cmath>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#include<sstream>
using namespace std;
typedef long long LL;
int N, M;
double f[110][610];
int main()
{
memset(f, 0, sizeof(f));
for(int i=1; i<=100; i++){
for(int j=i; j<=i*6; j++){
if(i == 1) f[i][j] = 1.0/6;
else{
for(int k=1; k<=6&&j-k>=i-1; k++){
f[i][j] += f[1][k]*f[i-1][j-k];
}
}
}
}
while(scanf("%d%d", &N, &M) == 2){
printf("%.2lf\n", 100*f[N][M]);
}
return 0;
}