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;
}