zoukankan      html  css  js  c++  java
  • poj 1037 前K项固定求方案数, 递推动态规划

      解决此类问题常见思路:

        为求以 A1, A2, ... , AK 为前K项的所有栏杆字典排序第K小的序列.

      这里给出其他牛人的介绍

    先抛开此题,用最简单的全排列问题来说明这种方法。如1,23,4的全排列,共有4!种,求第10个的排列是?
    
    先试首位是1,后234有3!=6种<10,说明1偏小,转化成以2开头的第(10-6=4)个排列,而3!=6 >= 4,说明首位恰是2。
    
    第二位先试1(1没用过),后面2!=2个<4,1偏小,换成3(2用过了)为第二位,总编号也再减去2!,剩下2了。而此时2!>=2,说明第二位恰好是3。
    
    第三位先试1,但后面1!<2,因此改用4。末位则是1了。
    
    这样得出,第10个排列是2-3-4-1。(从1计起)
    
    这种方法的核心在于求,给定前缀下后面总的排列数。全排列问题较易求。同时还需记录前面用过的数字。

      

      这里注意到, '高低起伏', 是相邻两个来决定的.

      所以我们状态开设为 G( n, h ) , 且有两种不同状态 up, down

      所以状态为

        G( n, h ).up  意义为 n个数,第一个以高度h开始,第一第二个呈上升的排列方案数

        G( n, h ).down  意义为 n个数,第一个以高度h开始,第一第二个呈下降的排列方案数 

      注意到题目要求 '高低起伏' 

      所以 ( i, i-1 ) 为上升(或下降) 则 ( i-1, i-2 ) 必定为 下降(或上升)

      所以 我们可以得出递推关系式:

        

       

       注意, 

          假设从 1...N中除去 A1,A2,...,Ak 项后剩余 B1,B2,..,B(n-k) ,

          那么方案数目则转换成了求, B1,B2,..,B(n-k)项 头两块木头 上升(或下降)的 栅栏数目.

          又因为所谓的'高低起伏'只是木头与木头的相对高度,而非绝对高度.

          所以对于 N个高度,我们去掉了其中的T后, 剩下N-1不同数字可以映射到 1到n-1.相对为1,2,..,n-1的全排列

          所以方案中的T,为1,2,...,n中的相对第T小. 

          这样做,我们在N变换的过程中可以重复利用前面计算好的值,而节省大量时间.

          还要注意.求A1时,因为字典序是down小,up大, 当down不满足时,也要减去其方案数.

    解题代码:

    View Code
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<map>
    #include<vector>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    
    
    typedef long long LL;
    struct node{
        LL up, down;
    }G[25][25];
    
    int p[25], n;
    LL C, N;
    bool vis[25];
    
    map< int,int > Mp;
    
    // 递归函数获取 后面K个,以高度H为开始的序列方案数
    
    void Getg()
    {
        memset(G,0,sizeof(G));    
        for(int i = 1; i <= 1; i++)
            G[1][i].down = G[1][i].up = 1;
        for(int i = 2; i <= 20; i++)
        {
            for(int T = 1; T <= i; T++)
            {
                for(int j = 1; j <= T-1; j++) // 长度I位置的T已经被去掉了
                        G[i][T].down += G[i-1][j].up;    
                for(int j = T; j <= i-1; j++) // 原本的T已经去掉,T+1映射成相对位置T,后面只有i-1个数
                        G[i][T].up += G[i-1][j].down;
            }    
        }
    }
    int main()
    {
        Getg();
    
        int T;
        scanf("%d", &T);
        while( T-- )
        {
            scanf("%d%lld", &n, &C);
            N = C;
            
            memset( vis, 0, sizeof(vis) );
            memset( p, 0, sizeof(p) );
            int clas;    
            // 单独处理 A1 位置 
            for(int h = 1; h <= n; h++)
            {
                vis[h] = true;
                p[1] = h;    
            
                LL tmp = G[n][h].down+G[n][h].up;
            //    printf("down=%lld,up=%lld\n",G[n][h].down, G[n][h].up);    
                if( tmp >= N ){
                    if( G[n][h].down >= N ) clas = 0;
                    else{
                        N -= G[n][h].down;    
                        clas = 1;
                    }    
                    break;
                }
                else    N -= tmp;    
                vis[h] = false;
            
            }
    //        printf("p[1] = %d, class = %d, N = %lld\n", p[1], clas, N );    
            // 处理 Ai 位置    
            for(int i = 2; i <= n; i++)
            {
                int cnt = 1; Mp.clear();
                for(int h = 1; h <= n; h++)
                    if( !vis[h] ) Mp[h] = cnt++;
    
                if( clas == 0 ) // (i-1,i) 为down, 则 (i,i+1)必须为 up
                {// G[i-1]位置为 down
                    //枚举 Ai
                    for(int h = 1; h < p[i-1]; h++)
                        if( !vis[h] ) //未被使用    
                        {
                            int T = Mp[h]; //相对高度第T小    
                            vis[h] = true;
                            p[i] = h;    
                                
                            LL tmp = G[n+1-i][T].up;
                            if( tmp >= N )
                            {
                                clas = 1;    
                                break;    
                            }
                            else N -= tmp;    
                            vis[h] = false;
                        }
                }        
                else // (i-1,i) 为up, 则 (i,i+1) 必须为 down
                {// G[i-1]位置为 up
                    for(int h = p[i-1]+1; h <= n; h++)
                        if( !vis[h] )    
                        {
                            int T = Mp[h];    
                            vis[h] = true;
                            p[i] = h;    
                            LL tmp = G[n+1-i][T].down;
                        //    printf("tmp = %lld\n", tmp );    
                            if( tmp >= N )
                            {
                                clas = 0;    
                                break;    
                            }
                            else N -= tmp;    
                            vis[h] = false;            
                        }
                }
    //            printf("p[%d] = %d, class = %d, N = %lld\n",i, p[i], clas, N );    
                
            }
            for(int i = 1; i <= n; i++ )
                printf( i == 1 ? "%d" : " %d", p[i] );    
            puts("");    
        }
        return 0;
    }
  • 相关阅读:
    Samba
    百度贴吧
    baidu.com关键字查询
    vim 删除每行开头结尾空格
    read line(逐行读取)
    pycharm 激活
    rsync 启动脚本
    收藏
    elk
    sql is null
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2855259.html
Copyright © 2011-2022 走看看