热身训练4 Article
在这个学期即将结束时,DRD开始写他的最后一篇文章。 DRD使用著名的Macrohard的软件World来写他的文章。 不幸的是,这个软件相当不稳定,它总是崩溃。 DRD需要在他的文章中写N个字符。 他可以在i+0.1的时候按一个键来输入一个字符,其中i是一个等于或大于0的整数。 但是在每一次i-0.1的时候,如果整数i严格大于0,世界可能会以概率p崩溃,DRD就会失去他的工作,所以他可能不得不从他最近保存的文章重新开始。 为了防止重复写,DRD可以在i时间按Ctrl-S来保存他的文件。 由于DRD使用的键盘很奇怪,按Ctrl-S需要按tmp个字符。 如果DRD已经输入了他的全部文章,他必须按Ctrl-S来保存文件。 由于《世界》经常崩溃,现在他正在向他的朋友ATM询问输入文章的最佳策略。 一个策略是以DRD需要按的预期键来衡量的。 请注意,DRD可以以足够快的速度按下一个键。
分析:
加入我们不用“保存”操作,我们可以O(n)的dp求出,写到第i个字符期望需要的打字数。
假如我们要进行k次保存操作,Ans=∑dp[xi] (1<=i<=k,xi表示第i段的长度,∑xi=n)+k*tmp。
对于每段的长度,我们贪心的来说,肯定越平均越好。
于是这道题大致就做完了,最终只需要枚举保存操作的次数即可。
#include<bits/stdc++.h> using namespace std; #define re register int double f[1000005]; int main() { int T;scanf("%d",&T); for(re cas=1;cas<=T;++cas) { int n, x; double p, ans=1e16; scanf("%d%lf%d",&n,&p,&x); memset(f, 0, sizeof(f)); for(re i=1;i<=n;++i) f[i] = (f[i-1]+1.0) / (1.0-p); for(re i=1;i<=n;++i) { int t = n/i, k = n%i; double ret = i * x + k*f[t+1] + (i-k)*f[t]; ans = min(ans, ret); } printf("Case #%d: %.6lf\n", cas, ans); } }