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]=\sum\limits_{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]=\sum\limits_{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\n",b[n]);
    }
    return  0;
}

原文地址:https://www.cnblogs.com/zhangjianjunab/p/11748685.html

时间: 2024-11-02 09:45:48

0x50 动态规划(0x5C 计数类DP)例题3:装饰围栏(题解)(计数类DP讲解,确定第k个排列)的相关文章

dp、sp 转换为 px 的工具类

/**     * dp.sp 转换为 px 的工具类     *      * @author fxsky 2012.11.12     *     */     public class DisplayUtil {         /**         * 将px值转换为dip或dp值,保证尺寸大小不变         *          * @param pxValue         * @param scale         *            (DisplayMetric

小结在一个java源文件当中可以有多个类,但是为什么只能有一个public类呢?而当这个类被修饰为public的话,为什么源文件名必须要与类名相同呢?

Java编程思想中的一段话: 当编写一个java源代码文件时,此文件通常被称为编译单元(有时也被称为转译单元).每个编译单元都必须有一个后缀名.java,而在编译单元内则可以有一个public类,该类的名称必须与文件的名称相同(包括大小写,但不包括文件的后缀名.java).每个编译单元只能有一个public类,否则编译器就不会接受.如果在该编译单元之中还有额外的类的话,那么在包之外的世界是无法看见这些类的,这是因为它们不是public类,而且它们主要用来为主public类提供支持. 理解: 每编

虚基类练习:动物(利用虚基类建立一个类的多重继承,包括动物(animal,属性有体长,体重和性别),陆生动物(ter_animal,属性增加了奔跑速度),水生动物(aqu_animal,属性增加了游泳速度)和两栖动物(amp_animal)。)

Description 长期的物种进化使两栖动物既能活跃在陆地上,又能游动于水中.利用虚基类建立一个类的多重继承,包括动物(animal,属性有体长,体重和性别),陆生动物(ter_animal,属性增加了奔跑速度),水生动物(aqu_animal,属性增加了游泳速度)和两栖动物(amp_animal).其中两栖动物保留了陆生动物和水生动物的属性. Input 两栖动物的体长,体重,性别,游泳速度,奔跑速度(running_speed) Output 初始化的两栖动物的体长,体重,性别,游泳速度

Atitit.&#160;衡量项目规模&#160;----包含的类的数量&#160;.net&#160;java类库包含多少类&#160;多少个api方法??

Atitit. 衡量项目规模 ----包含的类的数量 .net java类库包含多少类 多少个api方法?? 1 framework 4.5 (10万个api)1 2 Jdk8   57M1 3 Gialen项目2 4 播放器项目 3500个类2 5 Jquery 1.11 (api 305个)2 6 Wordpress 4.2   (函数api详单(约500个api)2 7 Html5  860个api2 7.1 Css2 8 Js  430个3 1 framework 4.5 (10万个ap

基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------&gt; 可以返回派生类对象的引用或指针

您查询的关键词是:c++primer习题15.25 以下是该网页在北京时间 2016年07月15日 02:57:08 的快照: 如果打开速度慢,可以尝试快速版:如果想更新或删除快照,可以投诉快照. 百度和网页 http://bbs.csdn.net/topics/380238133 的作者无关,不对其内容负责.百度快照谨为网络故障时之索引,不代表被搜索网站的即时页面. 首页 精选版块 移动开发 iOS Android Qt WP 云计算 IaaS Pass/SaaS 分布式计算/Hadoop J

1,OC语言的前世今生 ,2,OC语言入门,3,OC语言与C的差异,4,面向对象,5,类和对象的抽象关系,6,类的代码创建,7,类的成员组成及访问

1,OC语言的前世今生 , 一, 在20世纪80年代早期,布莱德.麦克(Brad Cox)设计了OC语言,它在C语言的基础上增加了一层,这意味着对C进行了扩展,从而创造出一门新的程序设计语言,支持对象的创建和操作. 二,1985年,被赶出苹果公司的乔帮主成立了Next公司; 三, 1988年,Next计算机公司获得了OC语言的授权,并发展了OC语言库和一个开发环境,1994年,Next计算机公司(同年更名为Next软件公司)和Sun公司针对NEXTSTEP系统联合发布了一个标准规范,名为OPEN

【C/C++学院】0819-/类的成员函数与const-mutable /构造与析构/拷贝构造deletedefault以及深浅拷贝/静态成员函数成员变量类在内存的存储默认参数/友元类以及友元函数

类的成员函数与const-mutable 成员函数 Fushu.h #pragma once #include <iostream> class fushu { public: int x; int y; public: fushu(); ~fushu(); void show(); inline void showall(int x, int y);//显式内联 void setxy(int x, int y);//编译器优化,默认隐式内联 void show(int x, int y);

java异常处理:建立exception包,建立Bank类,类中有变量double balance表示存款,Bank类的构造方法能增加存款,Bank类中有取款的发方法withDrawal(double dAmount),当取款的数额大于存款时,抛出InsufficientFundsException,取款数额为负数,抛出NagativeFundsException,如new Bank(100),

建立exception包,建立Bank类,类中有变量double  balance表示存款,Bank类的构造方法能增加存款,Bank类中有取款的发方法withDrawal(double dAmount),当取款的数额大于存款时,抛出InsufficientFundsException,取款数额为负数,抛出NagativeFundsException,如new Bank(100),表示存入银行100元,当用方法withdrawal(150),withdrawal(-15)时会抛出自定义异常. pa

OC基础(十)类的本质及isa指针和元类的简单介绍

一.类的基本概念: 1.类其实也是一个对象, 这个对象会在这个类第一次被使用的时候创建 2.只要有了类对象, 将来就可以通过类对象来创建实例对象 3.实例对象中有一个isa指针, 指向创建自己的类对象 4.类对象中保存了当前对象所有的对象方法 5.当给一个实例对象发送消息的时候, 会根据实例对象中的isa指针去对应的类对象中查找 6.所有类对象的继承关系就是元类对象的继承关系 二.isa指针 1.每一个对象都包含一个isa指针.这个指针指向当前对象所属的类. 2.[d bark];表示给d所指向

【日常学习】【划分DP】codevs1017 乘积最大题解

题目来源 2000NOIP 题目描述 Description 今年是国际数学联盟确定的"2000--世界数学年",又恰逢我国著名数学家华罗庚先生诞辰90周年.在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加.活动中,主持人给所有参加活动的选手出了这样一道题目: 设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大. 同时,为了帮助选手能够正确理解题意,主持人还举了如下的一