HDUOJ 4576 2013杭州邀请赛重现 Robot

来源:互联网 发布:淘宝怎么把好评转差评 编辑:程序博客网 时间:2024/05/16 19:49

传送门

题意:有一个长度为n的环,起始在1位置,m代表走的次数,下接m行,分别是每次步数,但是不知道是顺时针还是逆时针,最后问在 l 和 r 之间的概率。

思路:根据前一次在每个点的概率,推出下一次在每个点的概率,复杂度O(mn),最坏情况2亿次计算,4s想想差不多够用。第0次(即最开始)在1的概率是1,在其他位置的概率是0,这里用p[0][1]=1表示第0次在1的概率为1。拿第二组样列来举例子吧:第一次可以走1步,那么它可以从1走到2或5,然后p[1][2]+=p[0][1]/2,p[1][5]+=p[0][1]/2,这样第一次走完,就只可能在2和5啦,概率分别为1/2和1/2。然后考虑第二次,第二次走2步,从2走可能到4和5,于是p[2][4]+=p[1][2]/2,p[2][5]+=p[1][2]/2;从5走可以走到2或3,p[2][2]+=p[1][5]/2,p[2][3]+=p[1][5]/2。这步走完后,分别可以出现在2,3,4,5,概率分别为1/4,1/4,1/4,1/4。然后出现在4~4的概率自然就是1/4啦。

然而这不是现场赛,内存不是你想开多大就开多大的,按照上面思路要开至少p[1000000][200]大的float数组,我MLE了几次,之后就用滚动数组,即开p[2][200],每次前一步是p[0][x],计算下一步p[1][x],之后再将p[1][x]赋给p[0][x]做下一次计算。这里差不多就是时间换空间啦。

#include<iostream>#include<cstdio>#include<cstring>using namespace std;float p[2][201],ans;int m,n,l,r,a,ra,la;int main(){    while(scanf("%d%d%d%d",&n,&m,&l,&r))    {        if(n==0&&m==0&&l==0&&r==0)return 0;        memset(p,0,sizeof(p));        p[0][1]=1;        for(int i=1;i<=m;i++)        {            scanf("%d",&a);            for(int j=1;j<=n;j++)            {                if(p[0][j]==0)continue;                la=j-a;                while(la<=0)la+=n;                ra=j+a;                while(ra>n)ra-=n;                p[1][la]+=p[0][j]/2;                p[1][ra]+=p[0][j]/2;            }            for(int i=1;i<=n;i++)            {                p[0][i]=p[1][i];                p[1][i]=0;            }        }        ans=0;        for(int i=l;i<=r;i++)ans+=p[0][i];        printf("%.4f\n",ans);    }}