zoukankan      html  css  js  c++  java
  • 电梯(lift)

    问题描述(1S, 128M)
    无所事事的Cinzo决定用坐电梯的方式来打发时间。他住在一个N层的房子中,最底下为1层,最高处为N层。他从他家所在的第A层出发,并决定连续坐K次电梯。
    但由于迷信的缘故,B在中国被视为是不幸运的,所以整座楼并没有第B层。也是因为这个原因,如果Cinzo想从第X层出发到达第Y层,他希望Y能满足|X - Y| < |X - B|。
    每次电梯到达后,Cinzo都会将电梯所到的层数记录在小本子上;K次电梯都坐完后,他将得到一个长度为K的数列。现在,Cinzo想知道,他可能写出多少个不同的数列?

    输入格式(lift.in)
    一行四个整数,N,A,B,K,分别代表电梯的层数,Cinzo最初的位置,不幸运的层数,以及乘坐电梯的次数。

    输出格式(lift.out)
    一个整数,代表不同的数列数。(结果对1000,000,007取模)

    样例输入
    5 2 4 2

    样例输出
    2

    数据范围与约束
    对于20%的数据,N<=10, K<=5;
    对于60%的数据,N,K<=100;
    对于100%的数据,N,K<=5000。

    ························································································································································································································································
    这是一道(我觉得难的)dp题;
    用f[i][j]表示做了 i 次,到第 j 层的方案数。

    显然,f[i][j]可以由一步可以跳到的楼层转移过来。
    那么就可以得到转移方程:
    f[i][j]=Σf[i-1][j-dis~j+dis] ,(dis=abs(j-b)-1)
    直接一个一个地加,时间复杂度是O(n^2*k) , 这样可以过60%。

    我们可以发现,每一次需要加的这一段是连续的,(考虑出一个问题,在数组A[l,r]上都加上x,那么我们可以用差分,最后对记差分的数组求一遍前缀和就是A数组中真正的值。),那么我们就可以用 差分,以为每次f[i][],一开始都是0,所以直接对f[i][1~n]数组记差就可以了。这样时间复杂度是O(n*k)。

    我们可以发现,每次我们往下dp时,只用到前1层的数据,那么我们可以用滚动数组来记录,这样就可以降低空间复杂度了。
    需要注意一个问题,因为用的是滚动数组,所以要特别注意最后答案存在哪里。这可能是0分与100分的差别!

    #include<iostream>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cstdio>
    #include<queue>
    #include<cmath>
    #define MOD 1000000007
    #define LL long long
    using namespace std;
    int f[2][5009];//f[5009][5009]-->f[i][j]是第i次,到第j层,方案数 
    int n,a,b,k,ans;
    int l[5009],r[5009];//记录每个位置跳的边界 
    int main()
    {
        scanf("%d%d%d%d",&n,&a,&b,&k);
        for(int i=1;i<=n;i++)
        {
            int dis=abs(i-b)-1;
            l[i]=max(1,i-dis);r[i]=min(n,i+dis);
        }
        int e1=0,e2=1;
        f[0][a]=1;
        for(int i=1;i<=k;i++)
        {
            for(int j=1;j<=n;j++) f[e2][j]=0;
            for(int j=1;j<=n;j++)
            {
                (f[e2][l[j]]+=f[e1][j])%=MOD;
                (f[e2][r[j]+1]-=f[e1][j])%=MOD;
            }
            for(int j=1;j<=n;j++) (f[e2][j]+=f[e2][j-1])%=MOD;
            for(int j=1;j<=n;j++) (f[e2][j]-=f[e1][j])%=MOD;//减去不跳的情况 
    
            swap(e1,e2);
        } 
        for(int i=1;i<=n;i++) if(i!=b)
        (ans+=f[e1][i])%=MOD;
        ans=(ans+MOD)%MOD;//因为中间有减法,   以后也是加上好
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    linux全方位掌握一个命令--思路比方法更重要
    grep命令详解
    linux中的通配符与正则表达式
    sed命令使用详解归纳
    linux下命令行操作快捷键及技巧
    (原创)发布一个C++版本的ORM库SmartDB(一)
    (原创)C++11改进我们的程序之简化我们的程序(八)
    (原创)C++11改进我们的程序之简化我们的程序(七)
    (原创)C++11改进我们的程序之简化我们的程序(六)
    (原创)C++11改进我们的程序之简化我们的程序(五)
  • 原文地址:https://www.cnblogs.com/dfsac/p/7587882.html
Copyright © 2011-2022 走看看