zoukankan      html  css  js  c++  java
  • 兔子的排列 题解(分治+记忆化搜索)

    题目链接

    题目大意

    题目的意思就是一个序列本来是0,1,2...n-1.然后要你变成p[0],p[1],p[2]...p[n-1],通过交换相邻的两个数每两个相邻的数都可以交换一次,你可以选择交换的顺序.问你有多少种交换顺序?

    题目思路

    有两种方法,一种复杂度(O(n^5)) 一种复杂度(O(n^3))

    首先如果一个点换了,那么相当于切断了两边,则可以两边单独考虑

    则要想到分治

    (dp[l][r][bg][ed]) 代表区间l-r中开头为bg,结尾是ed,

    最不好理解的是为什么枚举断点时要乘 C(r-l-1,i-l)

    因为这个区间总共要交换r-l次,而中间交换了1次

    所以左边和右边总共交换次数为r-l-1次

    而左边的交换次数为i-l次

    则相当于要在r-l-1次中给左边的交换次数安排位置

    有点难理解,自己可以思考下

    优化到(O(n^3))的方法就是直接把dp数组变成(dp[l][r])直接省去两维

    因为你划分两段后你用前缀和算一下就能直接判断是否可以满足划分

    代码1

    #include<bits/stdc++.h>
    #define debug cout<<"I AM HERE"<<endl;
    using namespace std;
    typedef long long ll;
    const int maxn=50+5,inf=0x3f3f3f3f,mod=1e9+7;
    const int eps=1e-6;
    int n;
    int a[maxn];
    ll fac[maxn];
    ll dp[maxn][maxn][maxn][maxn];
    ll qpow(ll a,ll b){
        ll ans=1,base=a;
        while(b){
            if(b&1){
                ans=ans*base%mod;
            }
            base=base*base%mod;
            b=b>>1;
        }
        return ans;
    }
    void init(){
        fac[0]=1;
        for(int i=1;i<=50;i++){
            fac[i]=fac[i-1]*i%mod;
        }
    }
    ll C(int a,int b){ // 计算 C(a,b)
        return fac[a]*qpow(fac[b],mod-2)%mod*qpow(fac[a-b],mod-2)%mod;
    }
    ll dfs(int l,int r,int bg,int ed){
        if(dp[l][r][bg][ed]!=-1){
    
        }else if(l==r){ // 长度为1
            dp[l][r][bg][ed]=(a[l]==bg);
        }else{
            ll ans=0;
            for(int i=l;i<r;i++){
                int x1,y1,x2,y2;
                // 注意bg和ed已经改变了
                // [bg+1,ed-1]还没改变
                if(r-l==1){ // 长度为2
                    x1=y1=ed;
                    x2=y2=bg;
                }else if(i==l){
                    x1=y1=i+1;
                    x2=bg;
                    y2=ed;
                }else if(i==r-1){
                    x1=bg;
                    y1=ed;
                    x2=y2=i;
                }else{
                    x1=bg;
                    y1=i+1;
                    x2=i;
                    y2=ed;
                }
                ans=(ans+dfs(l,i,x1,y1)*dfs(i+1,r,x2,y2)%mod*C(r-l-1,i-l))%mod;
            }
            dp[l][r][bg][ed]=ans;
        }
        return dp[l][r][bg][ed];
    }
    int main(){
        init();
        memset(dp,-1,sizeof(dp));
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            a[i]++;
        }
        printf("%lld
    ",dfs(1,n,1,n));
        return 0;
    }
    
    

    代码2

    #include<bits/stdc++.h>
    #define debug cout<<"I AM HERE"<<endl;
    using namespace std;
    typedef long long ll;
    const int maxn=50+5,inf=0x3f3f3f3f,mod=1e9+7;
    const int eps=1e-6;
    int n;
    int a[maxn],b[maxn];// b->a;
    int prea[maxn],preb[maxn];
    ll fac[maxn];
    ll dp[maxn][maxn];
    ll qpow(ll a,ll b){
        ll ans=1,base=a;
        while(b){
            if(b&1){
                ans=ans*base%mod;
            }
            base=base*base%mod;
            b=b>>1;
        }
        return ans;
    }
    void init(){
        fac[0]=1;
        for(int i=1;i<=50;i++){
            fac[i]=fac[i-1]*i%mod;
        }
    }
    ll C(int a,int b){ // 计算 C(a,b)
        return fac[a]*qpow(fac[b],mod-2)%mod*qpow(fac[a-b],mod-2)%mod;
    }
    ll dfs(int l,int r){
        if(dp[l][r]!=-1){
    
        }else if(l==r){ // 长度为1
            dp[l][r]=(a[l]==b[l]);
        }else{
            ll ans=0;
            for(int i=l;i<r;i++){
                swap(b[i],b[i+1]);
                preb[i]+=b[i]-b[i+1];
                if(preb[i]-preb[l-1]==prea[i]-prea[l-1]&&preb[r]-preb[i]==prea[r]-prea[i]){
                    ans=(ans+dfs(l,i)*dfs(i+1,r)%mod*C(r-l-1,i-l))%mod;
                }
                preb[i]-=b[i]-b[i+1];
                swap(b[i],b[i+1]);
            }
            dp[l][r]=ans;
        }
        return dp[l][r];
    }
    int main(){
        init();
        memset(dp,-1,sizeof(dp));
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            a[i]++;
            b[i]=i;
            prea[i]=prea[i-1]+a[i];
            preb[i]=preb[i-1]+b[i];
        }
        printf("%lld
    ",dfs(1,n));
        return 0;
    }
    
    
  • 相关阅读:
    Delphi 2010下安装IOComp
    为自定义控件的某个属性添加像Winform控件中属性的注释
    C#WinForm仿qq窗体拖到windows窗体边上时,自动隐藏C#WinForm
    给图片添加水印,解决GIF添加水印的问题(无法从带有索引像素格式的图像创建 Graphics 对象)
    怎样把Image数据放入数据库
    取消IE“已限制此网页运行可以访问计算机的脚本
    sql 二进制文件的导入导出
    [转载]数据结构笔试题基础
    [转载]搜索算法(含基本搜索算法与深度搜索与广度搜索算法等思想)
    [转载]面试笔试总结
  • 原文地址:https://www.cnblogs.com/hunxuewangzi/p/14327875.html
Copyright © 2011-2022 走看看