hdu 4576 robot 2013 ACM-ICPC杭州赛区全国邀请赛——题目重现-1001-robot

题意:有一个机器人他的初始位置是1,在一个环形轨道上(轨道的长度n<=200),由于遥控器不太灵,机器人不太能准确的接受遥控的指令,遥控指令是让机器人顺时针或逆时针走w步,所以机器人有1/2的概率顺时针走w步,1/2的 概率逆时针走w步,题目就是要你算经过m(m<=1000000)次指令遥控后机器人最后停在某段轨道(L,R)的概率,这题我也没想出什么好办法,就暴力了,不断的WA,TLE,最后3500ms险过了(题目限时4000ms)

既然决定暴力,那么就是一步一步的算机器人走到轨道的某处的情况数了,可以一步步递推每次接到指令后就枚举轨道的位置,复杂度是n*m=2*10^8很凶残啊。。。。

输入格式是先输入 n,m,l,r,接下来再有m行输入指令的w值,若n,m,l,r,都是0则输入结束

需要注意的是所有的情况数将会非常庞大可以爆掉int,会WA,但是用long long 或double 或者float 都会TLE,没办法还是只能用int去保存情况总数,因为精度的要求不是特别高所以可以在他快要爆掉int的时候给他整体缩小一定的倍数,这样最后的比值是不会变太多的,ok,该说的都说完了

暴力开始代码如下:

注意用c++交会TLE,用G++交大概是3500ms

/*********
PRO: hdu 4576
TIT: Robot
DAT: 2013-08-10-13.22
AUT: UKean
EMA: [email protected]
*********/
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int  way[205],temp[205], l,r,m,n,walk;//way数组存的是当前的轨道上到每处的情况总数,temp存的是上一次的轨道上的情况
int main()
{
    while(scanf("%d%d%d%d",&n,&m,&l,&r))
    {
        if(n+m+l+r==0) break;
        memset(way,0,sizeof way);//初始化当前的轨道
        way[1]=1;//起点在 1
        for(int i=0;i<m;i++)
        {
            scanf("%d",&walk);
            copy(way,way+n,temp);
            memset(way,0,sizeof(way));
            for(int i=0;i<n;i++)
                if(temp[i])//上一步可以走到i则向左和右各走walk步
                {
                    way[(i+walk)%n]+=temp[i];//顺时针走
                    way[((i-walk)%n+n)%n]+=temp[i];//逆时针走
                }
            int f=0;//因为情况数太多结果是会溢出的,f是快溢出的标记
            for(int i=0;i<n;i++)
                if(way[i]>100000000) {f=1;break;}
            if(f) for(int i=0;i<n;i++)  way[i]/=10;//如果有溢出的可能就把整个所有的情况数都除以10
        }
        if(n==1) { printf("1.0000\n");continue;}//n为1是特殊情况要特判一下
        long long  sum=0,ans=0;//sum是总的情况数,ans是满足条件的情况数
        for(int i=0;i<n;i++) sum+=way[i];
        for(int i=l;i<=r;i++) ans+=way[i%n];
        printf("%.4lf\n",(1.0*ans)/(double)sum);
    }
    return 0;
}



无注释版

/*********
PRO: hdu 4576
TIT: robot
DAT: 2013-08-10-13.22
AUT: UKean
EMA: [email protected]
*********/
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<iostream>
using namespace std;
int  way[205];
int temp[205];
int l,r,m,n,walk;
int main()
{

    while(scanf("%d%d%d%d",&n,&m,&l,&r))
    {
        if(n+m+l+r==0) break;
        memset(way,0,sizeof way);
        way[1]=1;
        for(int i=0;i<m;i++)
        {
            scanf("%d",&walk);
            copy(way,way+n+2,temp);
            memset(way,0,sizeof(way));
            for(int i=0;i<n;i++)
                if(temp[i])
                {
                    way[(i+walk)%n]+=temp[i];
                    way[((i-walk)%n+n)%n]+=temp[i];
                }
            int f=0;
            for(int i=0;i<n;i++)
                if(way[i]>100000000) {f=1;break;}
            if(f)
            for(int i=0;i<n;i++)
                way[i]/=10;
        }
        if(n==1)
        {
            printf("1.0000\n");
            continue;
        }
        long long  sum=0,ans=0;
        for(int i=0;i<n;i++) sum+=way[i];
        for(int i=l;i<=r;i++) ans+=way[i%n];
        //cout<<"ans "<<ans<<" sum "<<sum<<endl;
        printf("%.4lf\n",(1.0*ans)/(double)sum);
    }


    return 0;
}

转载的别人的代码:我的太暴力,太没技术含量了,转一下大神的代码仰慕一下,哈哈

这个题目看网上好多题解都是直接O(n*m)卡过。我是这么做的。

对于m次操作,统计每个w的次数。然后对每个w做矩阵乘法。

这样直接做矩阵乘法是会TLE的。

又由于这里的矩阵很特殊,一次乘法可以降维成O(n^2)。

--------------------------

怎么降维的可以这样模拟下:

a b c      a b c     a*a+2bc  c*c+2ab   b*b+2ac

c a b  *  c a b =  b*b+2ac  a*a+2bc  c*c+2ab

b c a      b c a     c*c+2ab  b*b+2ac  a*a+2bc

注意到原矩阵的每一行(除了第一行)都是上一行向右平移一个单位的结果,而相乘得到的矩阵也满足这个性质。

那么做一次矩阵乘法的时候,就只用算出结果矩阵的第一行,然后下面的每一行直接可由上一行得到。

复杂度降为了O(n^2)。

-------------------------
 
 
 
 
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<vector>
#include<ctype.h>
#include<algorithm>
using namespace std;
typedef long long LL;
#define FF(i,x)    for(i=1;i<=x;i++)
const int N = 205;

double cMat[N][N],retMat[N][N];

void matrixMul1(double A[][N],double B[][N],int a,int b)
{
    double buff[N][N]={};
    int i,j,k;
    FF(i,a)    FF(k,a)    FF(j,b)
        buff[i][j] = buff[i][j] + A[i][k]*B[k][j];
    FF(i,a)    FF(j,b)    B[i][j]=buff[i][j];
}

void matrixMul2(double A[][N],double B[][N],int a,int b)
{
    double buff[N][N]={};
    for(int j=1;j<=a;j++)
    {
        for(int k=1;k<=a;k++)
            buff[1][j]+=A[1][k]*B[k][j];
    }
    for(int i=2;i<=a;i++)
    {
        for(int j=2;j<=a;j++)    buff[i][j]=buff[i-1][j-1];
        buff[i][1]=buff[i-1][a];
    }
    int i,j;
    FF(i,a)    FF(j,b)    B[i][j]=buff[i][j];
}

void matrixFastPow(int p,int n)
{
    for(;p;p>>=1)
    {
        if( p&1 )    matrixMul1(cMat,retMat,n,1);
        matrixMul2(cMat,cMat,n,n);
    }
}

int amount[105];

int main()
{
    int n,m,l,r;
    int w;

    while( scanf("%d%d%d%d",&n,&m,&l,&r),n||m||l||r )
    {
        memset(amount,0,sizeof(amount));
        for(int i=0;i<m;i++)
        {
            scanf("%d",&w);
            amount[w]++;
        }
        for(int i=1;i<=n;i++)
            if( i<l || i>r )    retMat[i][1]=0.0;
            else retMat[i][1]=1.0;
        for(int i=1;i<=100;i++)
            if( amount[i] )
            {
                for(int p=1;p<=n;p++)
                    for(int q=1;q<=n;q++)
                        cMat[p][q]=0.0;
                for(int j=1;j<=n;j++)
                {
                    int a=(j-i);
                    while( a<=0 )    a+=n;
                    int b=(j+i);
                    while( b>n )    b-=n;
                    cMat[j][a]+=0.5;
                    cMat[j][b]+=0.5;
                }
                matrixFastPow(amount[i],n);
            }

        printf("%.4f\n",retMat[1][1]);
    }
    return 0;
}


所以一个总的复杂度<O(n^2 * log1000000 * 100)=8千万.

不到2000msAC了^_^

 


你可能感兴趣的:(HDU,2013,robot,ACM-ICPC杭州赛区全国邀,题目重现-1001-robot,hdu4576)