zoukankan      html  css  js  c++  java
  • 0x50 动态规划(0x5C 计数类DP)例题3:装饰围栏(题解)(计数类DP讲解,确定第k个排列)

    计数类DP一般就是确定DP状态,DP出排名范围,然后不断逼近。

    题意

    题目链接

    【题目描述】
     有 N 块长方形的木板,长度分别为1,2,…,N,宽度都是1。
     现在要用这 N 块木板组成一个宽度为 N 的围栏,满足在围栏中,每块木板两侧的木板要么都比它高,要么都比它低。
     也就是说,围栏中的木板是高低交错的。
     我们称“两侧比它低的木板”处于高位,“两侧比它高的木板”处于低位。
     显然,有很多种构建围栏的方案。
     每个方案可以写作一个长度为N的序列,序列中的各元素是木板的长度。
     把这些序列按照字典序排序,如下图所示,就是 N=4 时,所有满足条件的围栏按照木板长度的字典序排序后的结果。 
    
     现在给定整数C,求排名为C的围栏中,各木板的长度从左到右依次是多少。 
    注:两侧的木板指的是相邻的两块木板 
    【输入格式】
     第一行包含整数K,表示一共有K组数据。
     接下来K行,每行包含一组数据,包括两个整数N和C。 
     【输出格式】
     每组数据输出一行结果,结果表示排名为C的围栏中,各木板的长度从左到右排成的序列。
     同行数据用空格隔开。 
     【数据范围】
    1<=N<=20
     0<C<2^63 
    【输入样例】
    2
     2 1
     3 3 
    【输出样例】
    1 2
     2 3 1 
    

    题解

    (f[i][j][0/1]),分别表示的是点集在(1)~((n-i+1)),第一个位置选的是(j)且第一个位置是(down(0)/up(1))的时候的方案数。

    那么很明显的一个状态转移方程是:
    (f[i-1][j][0]=sumlimits_{k=j}^{n-i+1}f[i][k][1])

    意思就是把(s(i))点集中的(j)~(n-i+1)(1),把(j)插到原本(j)的位置上,就成了(s(i-1))的点集了。

    那么很显然:
    (f[i-1][j][1]=sumlimits_{k=1}^{i-1}f[i][k][0])

    然后我们可以对于第(1)位的数字,通过看排名范围来确定第一位是什么数字,(up)还是(down),然后后面也一样慢慢逼近就行了。

    #include<cstdio>
    #include<cstring>
    #define  N  30
    using  namespace  std;
    typedef  long  long  LL;
    LL  f[N][N][2],m;//0为up,1为down
    int  n; 
    int  a[N];
    inline  int  findkth(int  k)
    {
        int  x=0;
        for(int  i=1;i<=n;i++)
        {
            x+=a[i];
            if(x==k)
            {
                a[i]=0;
                return  i;
            }
        }
    }
    int  b[N];
    int  main()
    {
        int  T;scanf("%d",&T);
        while(T--)
        {
            memset(f,0,sizeof(f));
            scanf("%d%lld",&n,&m);
            for(int  i=1;i<=n;i++)a[i]=1;
            f[n][1][0]=f[n][1][1]=1;
            for(int  i=n-1;i>=1;i--)
            {
                for(int  j=n-i+1;j>=1;j--)
                {
                    //现在处理的是up的情况
                    for(int  k=1;k<j;k++)f[i][j][1]+=f[i+1][k][0];
                    for(int  k=n-i+1;k>=j;k--)f[i][j][0]+=f[i+1][k][1];
                }
            }
            //计数DP
            int  type,id;
            for(int  i=1;i<=n;i++)//确定第一位是多少,up还是down 
            {
                if(f[1][i][1]<m)m-=f[1][i][1];
                else{b[1]=findkth(i);id=i;type=1;break;}
                if(f[1][i][0]<m)m-=f[1][i][0];
                else{b[1]=findkth(i);id=i;type=0;break;}
            }
            for(int  i=2;i<=n;i++)
            {
                int  st=1,ed=n-i+1;type^=1;
                if(type==0)ed=id-1;
                else  st=id;
                for(int  j=st;j<=ed;j++)
                {
                    if(f[i][j][type]<m)m-=f[i][j][type];
                    else{b[i]=findkth(j);id=j;break;}
                }
            }
            for(int  i=1;i<n;i++)printf("%d ",b[i]);
            printf("%d
    ",b[n]);
        }
        return  0;
    }
    
  • 相关阅读:
    c++读写MySQL
    感叹游戏行业的飞速发展
    和真正的程序员在一起是怎样的体验
    程序媛是怎样找老公的
    IO和socket编程
    郁金香搜索引擎的方案
    实现一个自己的搜索引擎的初始规划
    JVM知识在离线数据中的运用
    看Lucene源码必须知道的基本规则和算法
    看Lucene源码必须知道的基本概念
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/11748685.html
Copyright © 2011-2022 走看看