http://acm.hust.edu.cn/vjudge/problem/20869
见蓝书p143
题意:我们都知道,任何一个数都可以分解为若干个素数的乘积。每次我们随机选择一个比当前数小的素数,如果当前数能被该素数整除,则当前数变为 当前数 / 该素数的值,反之当前数不变。重复随机选择,直到当前数变为1.问随机选择次数的期望。
思路:用递归的方法(大雾) 可以将当前的函数f(x) 分解。得到f(x) =... 得到关于f(x)的一个自上而下的公式。(注意此处是根据全期望公式和数学期望的线性,不必证明,会用即可)利用记忆化搜索即可
另:记忆化搜索要记得更改dp的值。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
double dp[1000005];
bool vis[1000005];
bool isPrime[2000005];
LL primeList[1000005],primeCount = 0;
void primeInit(LL n)
{
memset(isPrime,true,sizeof(isPrime));//初始化认为全部是素数
int m = sqrt(n + 0.5);
for(int i = 2; i <= m; i ++)
{
if(isPrime[i])//判断是素数
{
for(int j = i * i; j <= n; j += i)
{
isPrime[j] = false;
}
}
}
for(int i = 2; i <= n; i ++)
{
if(isPrime[i])
{
primeList[primeCount] = i;
primeCount ++;
}
}
}
double dfs(int a)
{
if(a == 1)
return 0.0;
if(vis[a] == true)
return dp[a];
double ans = 0.0;
vis[a] = true;
int p = 0,g = 0;
while( p < primeCount)
{
if(primeList[p] > a)
{
break;
}
if(a % primeList[p] == 0)
{
ans += dfs(a / primeList[p]);
g ++;
}
p ++;
}
if(g == 0)
{
return 0;
}
ans = (ans + p) / g;
dp[a] = ans;
return ans;
}
int main()
{
//freopen("in.txt","r",stdin);
primeInit(2000000);
int T;
scanf("%d",&T);
for(int t = 1; t <= T; t ++)
{
int x;
scanf("%d",&x);
memset(vis,false,sizeof(vis));
memset(dp,0,sizeof(dp));
double ans = dfs(x);
printf("Case %d: %f\n",t,ans);
}
return 0;
}