实验室

题目描述:
LYK 在一幢大楼里,这幢大楼共有 n 层, LYK 初始时在第 a 层上。
这幢大楼有一个秘密实验室,在第 b 层,这个实验室非常特别,对 LYK 具有约束作用,即若 LYK 当前处于 x 层,当它下一步想到达 y 层时,必须满足|x-y|<|x-b|,而且由于实验室是不对外开放的,电梯无法停留在第 b 层。
LYK 想做一次旅行,即它想按 k 次电梯,它想知道不同的旅行方案个数有多少个。
两个旅行方案不同当前仅当存在某一次按下电梯后停留的楼层不同。
输入格式:
一行 4 个数, n,a,b,k。
输出格式:
一个数表示答案,由于答案较大,将答案对 1000000007 取模后输出。
输入样例 1
5 2 4 1
输出样例 1
2
输入样例 2
5 2 4 2
输出样例 2
2
输入样例 3
5 3 4 1
输出样例 3
0
数据范围:
对于 20%的数据 n,k<=5。
对于 40%的数据 n,k<=10。
对于 60%的数据 n,k<=500。
对于 90%的数据 n,k<=2000。
对于 100%的数据 n,k<=5000。

暴力搜索:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5010;
const int mod=1000000007;
int n,a,b,k,ans;
void dfs(int x,int y)
{
    if(x==k+1)
    {
        ans=(ans+1)%mod;
        return;
    }
    for(int i=1;i<=n;i++)
    if(abs(y-i)<abs(y-b)&&i!=y)
    dfs(x+1,i);
}
int main()
{
    scanf("%d%d%d%d",&n,&a,&b,&k);
    dfs(1,a);
    printf("%d",ans);
    return 0;
}

记忆化搜索:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5010;
const int mod=1000000007;
int n,a,b,k,ans,f[maxn][maxn];
int dfs(int x,int y)
{
    if(x==k+1) return 1;
    for(int i=1;i<=n;i++)
    if(abs(y-i)<abs(y-b)&&i!=y)
    {
        if(f[x+1][i]) f[x][y]=f[x][y]+f[x+1][i];
        else          f[x][y]=f[x][y]+dfs(x+1,i);
    }
    return f[x][y];
}
int main()
{
    scanf("%d%d%d%d",&n,&a,&b,&k);
    f[1][a]=dfs(1,a);
    printf("%d",f[1][a]);
    return 0;
}

O(n^3)动规+压维

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5010;
const int mod=1000000007;
int n,a,b,k,ans,f[2][maxn];
int main()
{
    scanf("%d%d%d%d",&n,&a,&b,&k);
    for(int i=1;i<=n;i++)
    f[k+1&1][i]=1;
    for(int i=k;i>=1;i--)
    {
        for(int j=1;j<=n;j++)
        f[i&1][j]=0;
        for(int j=1;j<=n;j++)
          for(int l=1;l<=n;l++)
          if(abs(j-l)<abs(j-b)&&l!=j)
          f[i&1][j]=(f[i&1][j]+f[i+1&1][l])%mod;
    }
    printf("%d",f[1][a]);
    return 0;
}

优化动规
思路:
搜索–>记忆化搜索–>动规 O(n^3)–>动规 O(n^2)
dp状态
f[i][j]表示第i次停在j的方案数
倒推 边界为f[k+1][j]=1(j!=b)
因为最后可以停在除了b的所有地方
f[i][j]+=f[i+1]<a href="http://t.zoukankan.com/abs%28j-l%29l
本来应该循环但超时
令x=abs(j-b)
abs(j-l)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=1000000007;
const int maxn=5010;
int n,a,b,k,ans,f[2][maxn],sum[maxn];
int init()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int main()
{
    freopen("lab.in","r",stdin);
    freopen("lab.out","w",stdout);
    n=init();a=init();b=init();k=init();
    for(int i=1;i<=n;i++)
    {
        if(i!=b)f[k+1&1][i]=1;
        sum[i]=(sum[i-1]+f[k+1&1][i])%mod;
    }
    for(int i=k;i>=1;i--)
    {
        for(int j=1;j<=n;j++)
        f[i&1][j]=0;
        for(int j=1;j<=n;j++)
        {
            if(j==b) continue;
            int cha=abs(j-b)-1;
            int l=max(0,j-cha),r=min(n,j+cha);
            if(l>r) continue;
            f[i&1][j]=((sum[r]-sum[l-1])%mod+mod)%mod;
            f[i&1][j]=((f[i&1][j]-f[i+1&1][j])%mod+mod)%mod;
        }
        for(int j=1;j<=n;j++)
        sum[j]=(sum[j-1]+f[i&1][j])%mod;
    }
    printf("%d",f[1&1][a]);
    fclose(stdin);fclose(stdin);
    return 0;
}