《训练指南》 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