《训练指南》 P143
概率DP,数学问题
Dilu have learned a new thing about integers, which is - any positive integer greater than 1 can bedivided by at least one prime number less than or equal to that number. So, he is now playing withthis property. He selects a number N. And he calls this D.In each turn he randomly chooses a prime number less than or equal to D. If D is divisible by theprime number then he divides D by the prime number to obtain new D. Otherwise he keeps the oldD. He repeats this procedure until D becomes 1. What is the expected number of moves required forN to become 1.[We say that an integer is said to be prime if its divisible by exactly two different integers. So, 1 is nota prime, by definition. List of first few primes are 2, 3, 5, 7, 11, ...]
Input
Input will start with an integer T (T ≤ 1000), which indicates the number of test cases. Each of thenext T lines will contain one integer N (1 ≤ N ≤ 1000000).
Output
For each test case output a single line giving the case number followed by the expected number of turnrequired. Errors up to 1e-6 will be accepted.
Sample Input
3
1
3
13
Sample Output
Case 1: 0.0000000000
Case 2: 2.0000000000
Case 3: 6.0000000000
DP关键点在于dp[i] 有可能会转移回自己。此时需要进行数学上的变换,使得dp[i] 在等式的左边。
dp[i] = 1 + dp[i]*( 1- divn/ptot ) + sigma( dp[ i/div ] ) / ptot ;
dp[i] = ( ptot + sigma( dp[ i/div ] ) ) / divn;
ptot为小于 i 的质数个数, divn 为能整除 i 的 质数个数, div为能整除 i 的质数。
另外 maxn 比较大,我第一次用的预处理,就连本机都爆了。改成记忆化搜索可以只计算需要的值,就能A。似乎一般的DP都可以用记忆化搜索的形式来进行,既提高效率,又方便理解,学到了。
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
const int maxn = 1000010, prinum = 99999;
bool nprime[maxn];
int plist[1500000];
int lp;
double dfs(int);
double dp[maxn];
bool dvis[maxn];
int main()
{
for(int i=2; i<=1001; i++)
{
if(!nprime[i])
{
int now = i*2;
while(now < maxn)
{
nprime[now] = 1;
now += i;
}
}
}
for(int i=2; i<maxn; i++)
if(!nprime[i])
{
lp++;
plist[lp] = i;
}
dp[1] = 0;
dvis[1] = 1;
int T;
scanf("%d", &T);
for(int i=1; i<=T; i++)
{
int n;
scanf("%d", &n);
printf("Case %d: %.6lf\n", i, dfs(n));
}
}
double dfs(int now)
{
if(dvis[now]) return dp[now];
dvis[now] = 1;
int tot = 0, divn = 0;
double ans = 0;
for(int i=1; i<=lp&&plist[i]<=now; i++)
{
tot++;
if(now%plist[i] == 0)
{
divn++;
ans += dfs(now/plist[i]);
}
}
ans = (ans+tot)/divn;
dp[now] = ans;
return ans;
}
2015.11.2