SGU 422 Fast Typing(概率DP)

 

题目大意

 

某人在打字机上打一个字符串,给出了他打每个字符出错的概率 q[i]。 打一个字符需要单位1的时间,删除一个字符也需要单位1的时间。在任意时刻,他可以花 t 的时间检查整个打出来的字符串,并且从当前光标删除到第一个出错的位置(留下的字符串要么为空,要么每个字符都是对的)。问你,他正确的打完该字符串最少需要花费的时间的期望值是多少

字符串的长度小于等于3000

题意有点绕,具体可以看看原题是怎么描述的

 

做法分析

 

首先确定这是一个概率DP的问题。

定义状态 f[i] 表示正确的打完前 i 个字符以后,最少还需要多少时间正确的打完所有字符

可以这样转移:往后连续打 j 个字符之后再来花时间检查字符串,并且删除,具体的转移方程就是这样的:

        对于一个具体的 j

        令 g[i][j]= j+t

                    +q[i+1]*(f[i]+j)  // 在第 i+1 位出错

                    +p[i+1]*q[i+2]*(f[i+1]+j-1)  // 在第 i+2 位出错

                    +p[i+1]*p[i+2]*q[i+3]*(f[i+2]+j-2)  // 在第 i+3 位出错

                    +......

                    +p[i+1]*p[i+2]*p[i+3]*...*q[i+j]*(f[i+j-1]+1)  // 在第 i+j 位出错

                    +p[i+1]*p[i+2]*p[i+3]*...*p[i+j]*f[i+j]  // 连续打的 k 个字符全部正确

上式中,p[i] 表示第 i 个字符正确的概率,q[i] 表示第 i 个字符错误的概率,p[i]+q[i]=1

考虑到上式需要移项来解出 f[i] 的具体表达式,而移到等式右边的始终是 q[i+1]*f[i],不妨对 g[i][j] 稍作修改

        令 g[i][j]= j+t

                    +q[i+1]*j  // 在第 i+1 位出错

                    +p[i+1]*q[i+2]*(f[i+1]+j-1)  // 在第 i+2 位出错

                    +p[i+1]*p[i+2]*q[i+3]*(f[i+2]+j-2)  // 在第 i+3 位出错

                    +......

                    +p[i+1]*p[i+2]*p[i+3]*...*q[i+j]*(f[i+j-1]+1)  // 在第 i+j 位出错

                    +p[i+1]*p[i+2]*p[i+3]*...*p[i+j]*f[i+j]  // 连续打的 k 个字符全部正确

        那么,我们就有:f[i]=min{ g[i][j] }/(1-q[i+1])=min{ g[i][j] }/p[i+1],1≤j≤n-i

        初始状态:f[n]=0

        目标状态:f[0]

虽然状态转移方程推出来了,但是不要急,注意数据范围:n≤3000

按照上面的式子 DP 的话,这个做法是 n3 的复杂度,还需要做一些优化

我们观察 g[i][j]g[i][j+1] 的关系:

         g[i][1]=1+t+q[i+1]+p[i+1]*f[i+1]

        g[i][2]=2+t+q[i+1]*2+p[i+1]*q[i+2]*(f[i+1]+1)+p[i+1]*p[i+2]*f[i+2]

                 =g[i][1]+1+q[i+1]+p[i+1]*p[i+2]*f[i+2]+(p[i+1]*q[i+2]-p[i+1])*f[i+1]+p[i+1]*q[i+2]

                 =g[i][1]+1+q[i+1]+p[i+1]*q[i+2]+p[i+1]*p[i+2]*f[i+2]-p[i+1]*p[i+2]*f[i+1]

                 =g[i][1]+1+1-p[i+1]*p[i+2]+p[i+1]*p[i+2]*f[i+2]-p[i+1]*p[i+2]*f[i+1]

                 =g[i][1]+2+p[i+1]*p[i+2]*(f[i+2]-f[i+1]-1)

同理:g[i][3]=g[i][2]+2+p[i+1]*p[i+2]*p[i+3]*(f[i+3]-f[i+2]-1)

推到这里,规律已经呼之欲出了。运用这个规律,便可以省去一个循环,把 n3 变成 n2 

 

写的那么复杂,其实代码就几行...

非常好的一道题目,赞一个!

 

参考代码

 

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int N=3006;

int n, t;
double p[N], f[N];

int main()
{
    while(scanf("%d%d", &n, &t)!=EOF)
    {
        for(int i=1; i<=n; i++) scanf("%lf", &p[i]), p[i]=1-p[i];
        f[n]=0;
        for(int i=n-1; i>=0; i--)
        {
            double cur=t+1+(1-p[i+1])+p[i+1]*f[i+1];
            double temp=p[i+1];
            f[i]=cur;
            for(int j=2; i+j<=n; j++)
            {
                temp*=p[i+j];
                cur+=2-temp+temp*f[i+j]-temp*f[i+j-1];
                f[i]=min(f[i], cur);
            }
            f[i]/=p[i+1];
        }
        printf("%.8lf\n", f[0]);
    }
    return 0;
}
SGU 422

 

题目连接 & AC通道

 

SGU 422 Fast Typing

 

 

 

转载于:https://www.cnblogs.com/zhj5chengfeng/p/3189876.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于计算机专业的学生而言,参加各类比赛能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比赛促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比赛通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比赛都要求参赛者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比赛鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比赛可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比赛是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比赛提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比赛可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比赛的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比赛对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值