【题意】:一篇文章n个字符,每打一个字符(包括退格)需要1个单位时间。时不时可以用t个时间去检查错误发现错误,用退格键退到第一个错误的地方,然后重新打。给出每个字符错误的概率,问打完这篇文章所需最小时间的期望。

【题解】:设dp[i]表示前i个字符已经正确打完,从第i+1个字符到结束所需的最小时间的期望。
               显然有dp[n] = 0,然后最终答案就是dp[0]了。
               枚举从i+1个字符开始打,打完k个字符就检查一次。
               dp[i] = min{(t+k) + q[i+1]*(dp[i]+k) + p[i+1]q[i+2]*(dp[i+1]+k-1) + …… + p[i+1]p[i+2]……q[i+k]*(dp[i+k-1]+1) + p[i+1]p[i+2]……p[i+k]*(dp[i+k]) } ,其中 1 <= k <= n - i。
               p[i] 是正确的概率,q[i] 是错误的概率。
               转移的意思是:一次过打k个字符,然后用 t 时间去检查,最后用退格键退回到第一个错误的位置。
               注意这个转移方程,很容易发现这是O(n*n*n)的。
                              从哪个开始打起 i
                                    一次打了多少个 j
                                          第一个错误的位置 k
               O(n*n*n)的做法明显会TLE的,但是利用数学知识,我们可以化简掉一维。最终的时间复杂度为O(n*n)。

【代码】:
 1 #include "iostream"
 2 #include "cstdio"
 3 #include "cstring"
 4 using namespace std;
 5 #define maxn 3050
 6 int n, t;
 7 double dp[maxn], p[maxn], q[maxn];
 8 
 9 void solve() {
10     dp[n] = 0;
11     for(int i = n - 1; i >= 0; i--) {
12         double c = p[i+1];
13         double sum = t + 1 + q[i+1+ dp[i+1* p[i+1];
14         dp[i] = sum;
15         for(int j = 2; j <= n - i; j++) {
16             c *= p[i+j];
17             sum = sum + 2 - c + c * dp[i+j] - c * dp[i+j-1];
18             dp[i] = min(dp[i], sum);
19         }
20         dp[i] /= p[i+1];
21     }
22     printf("%.6f\n", dp[0]);
23 }
24 
25 int main() {
26     while(~scanf("%d%d"&n, &t)) {
27         for(int i = 1; i <= n; i++) {
28             scanf("%lf"&q[i]);
29             p[i] = 1 - q[i];
30         }
31         solve();
32     }
33     return 0;
34 }
35