uva 11762 数学期望+记忆化搜索

题目大意:给一个正整数N,每次可以在不超过N的素数中随机选择一个P,如果P是N的约数,则把N变成N/p,否则N不变,问平均情况下需要多少次随机选择,才能把N变成1?

分析:根据数学期望的线性和全期望公式可以为每个状态列出一个方程,例如: f(x)=1+f(6)*1/3+f(3)*1/3+f(2)*1/3

等式右边的最前面的“1”是指第一次转移,而后面的几项是后续的转移,用全期望公式展开,一般地,设不超过x的素数有p个,其中有g个是x的因子,则

f(x)=1+f(x)*(1-g/p)+Σf(x/y)/p

边界f(1)=0。移项后整理得

f(x)=(Σf(x/y)+p)/g

因为x/y<x,可以用记忆化搜索的方式计算f(x)。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std; 

const int Max=1000005;
int prime[100000],Num;
bool flag[Max],vis[Max];
double f[Max];

void Init()
{
    int i,j;
    Num=0;
    for(i=2;i<Max;i++)
    {
        if(flag[i]) prime[Num++]=i;
        for(j=0;j<Num && i*prime[j]<Max;j++)
        {
            flag[i*prime[j]]=false;
            if(i%prime[j]==0) break;
        }
    }
}

double dp(int x)
{
    if(x==1) return 0.0;
    if(vis[x]) return f[x];
    vis[x]=true;
    double sum=0;
    int p=0,g=0;
    for(int i=0;i<Num && prime[i]<=x;i++)
    {
        p++;
        if(x%prime[i]==0)
        {
            g++;
            sum+=dp(x/prime[i]);
        }
    }
    sum=(sum+p)/g;
    return f[x]=sum;
}

int main()
{
    memset(flag,true,sizeof(flag));
    memset(vis,false,sizeof(vis));
    memset(f,0.0,sizeof(f));
    Init();
    int t,i,n;
    scanf("%d",&t);
    for(i=1;i<=t;i++)
    {
        scanf("%d",&n);
        printf("Case %d: %.10lf
",i,dp(n));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiong-/p/3259559.html