题目大意

给定n个数字,规定一种 cute 排序:序列中的数字大小为严格的波浪形,即 a[0] > a[1] < a[2] > a[3] < .... 或者 a[0] < a[1] > a[2] < a[3] .....。对于N个数字来说,可以构成多个cute序列,这些序列按照字典序进行排序,求出第k个序列。

题目分析

一、求字典序的第i个排列

直接一位一位枚举答案!从前到后枚举求得每一位:枚举一位时,计算在这样的前缀下,后面的总的排列数。如果严格小于总编号,则该位偏小,换更大的数,同时更新总编号;若大于等于,则该位恰好,枚举下一位,总编号不用更新。

二、使用动态规划

由于题目要求按照字典序的第k个cute序列,因此我们需要在字典序中,n个数字构成的cute序列以第i大为开头的有多少个。这样一个计数问题,有子结构 + 无后效性(需要进一步证明), 因此考虑使用动态规划。
    一般使用动态规划来解决问题需要问题满足几个条件: 
(1)可以划分子问题,子问题与总问题相似 
(2)无后效性 
    由n个数字构成的cute序列(波浪形序列)中,其连续的n-1个数字肯定也是cute序列; 
    无后效性,在设计状态,并用动归数组dp表示状态、推演状态的时候,需要保证当前点以后的状态只和当前点的状态有关,而与当前点是如何到达(未来的状态只和当前点的当前数值有关,和过去到当前点的路径的无关)。

首先考虑 A[n] 表示n个数字构成的cute序列的总数,显然太粗糙,不知道n个数字之间的关系,无法进行状态推演; 
    然后考虑 A[n][i] 表示由n个数字构成的,且以n个数字中第i大为开头的cute序列的总数,这样来进行状态推演的时候,A[n][i] = sum-of(A[n-1][k]),选择哪些k,和i和k的大小关系有关,因此不能保证无后效性; 
    因此考虑使用 Up[n][i] 表示n个数字构成的,且以第i大为首的上升序列(a[1] > a[0])的个数;Down[n][i]表示n个数字构成的,以第i大为首的下降序列(a[1] < a[0])的个数,这样,就有递推关系:

    for (int k = i; k <= m - 1; k++){
Up[m][i] += Down[m - 1][k];
}
for (int k = 1; k < i; k++){
Down[m][i] += Up[m - 1][k];
}

实现 (c++)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
#define MAX_COL_NUM 22
long long int Up[MAX_COL_NUM][MAX_COL_NUM];
long long int Down[MAX_COL_NUM][MAX_COL_NUM]; int main(){
int T, N;
long long int C;
scanf("%d", &T); //用动态规划,先求出dp数组。
//Up表示开始是上升(即A[1] > A[0]) 的波浪数组, Down表示开始是下降的波浪数组
//Up[n][i] 表示有n个数组成的序列,将第i大的数作为第一位的上升序列的个数
//Down[n][i] 表示由n个数组成的序列,将第i大的数作为第一位的下降序列个数
memset(Up, 0, sizeof(Up));
memset(Down, 0, sizeof(Down)); Up[1][1] = 1;
Down[1][1] = 1;
for (int m = 1; m <= MAX_COL_NUM - 1; m++){
for (int i = 1; i <= m; i++){
for (int k = i; k <= m - 1; k++){
Up[m][i] += Down[m - 1][k];
}
for (int k = 1; k < i; k++){
Down[m][i] += Up[m - 1][k];
}
}
} while (T--){
scanf("%d %llu", &N, &C); //候选序号,存放在vector中,便于删除
vector<int> candidates;
candidates.push_back(0);
for (int m = 1; m <= N; m++){
candidates.push_back(m);
} int result[MAX_COL_NUM]; //存放最后求出的序列
int n = N;
long long int left = C; //字典序第k大的序列
int next_dir = 2; //下一次选用的首数字和第二个数字构成上升还是下降序列,由之前序列的趋势决定
//0, 下降; 1上升; 2 both
//开始设为2,表示总序列的第一个和第二个之间的关系不明确
while (n >= 1){
int k = 1;
//n 表示,此次循环是在n个数中选择
//k 表示,此次选择n个数的第k大(这n个数放在 vector candidate中)去构成序列
while (k <= n){
if (next_dir == 0 && candidates[k] > result[N-n-1]){
if (left > Down[n][k]){
left -= Down[n][k];
}
else{
break;
}
} if (next_dir == 1 && candidates[k] < result[N-n-1]){
if (left > Up[n][k]){
left -= Up[n][k];
}
else{
break;
}
} if (next_dir == 2){
if (left > (Up[n][k] + Down[n][k])){
left -= (Up[n][k] + Down[n][k]);
}
else{
break;
}
}
k++;
}
if (k > n)
k = n;
result[N - n] = candidates[k]; next_dir = ! next_dir; //波浪形数组,方向取反 //当选择出来第一个数字之后,可以根据 left (剩余的序号)以及 Down[n][k](以选择出来的数字为开头的下降序列的个数 ) 决定
//如果 剩余的序号 小于等于 以选择出来的数字为开头的下降序列总数,则说明 第一个数字和第二个数字为下降,之后的next_dir 为上升
//否则,为下降
if (n == N){
if (left <= Down[n][k])
next_dir = 1;
else{
left -= Down[n][k];
next_dir = 0;
} } //从候选数组中删除已经选择出来的那个数
candidates.erase(candidates.begin() + k);
n --;
}
for (int i = 0; i < N; i++){
printf("%d ", result[i]);
}
printf("\n");
}
return 0;
}

poj_1037 动态规划+字典序第k大的更多相关文章

  1. SPOJ Lexicographical Substring Search 求字典序第k大子串 后缀自动机

    题目传送门 思路:按字典序,小的字符优先选取.对于一个字符,如果以这个字符开头的子串大于等于k个,那说明这个字符是应该选的,并且选完之后,可能还要继续选.如果以这个字符开头的子串小于k个,说明这个字符 ...

  2. 前k大金币(动态规划,递推)

    /* ///题解写的很认真,如果您觉得还行的话可以顶一下或者评论一下吗? 思路: 这题复杂在要取前k大的结果,如果只是取最大情况下的金币和,直接 动态规划递归就可以,可是前k大并不能找出什么公式,所以 ...

  3. hdu 5008 查找字典序第k小的子串

    Boring String Problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Ot ...

  4. Permutation UVA - 11525(值域树状数组,树状数组区间第k大(离线),log方,log)(值域线段树第k大)

    Permutation UVA - 11525 看康托展开 题目给出的式子(n=s[1]*(k-1)!+s[2]*(k-2)!+...+s[k]*0!)非常像逆康托展开(将n个数的所有排列按字典序排序 ...

  5. 后缀自动机求字典序第k小的串——p3975

    又领悟到了一点新的东西,后缀自动机其实可以分为两个数据结构,一个是后缀树,还有一个是自动机 后缀树用来划分endpos集合,并且维护后缀之间的关系,此时每个结点代表的是一些后缀相同且长度连续的子串 自 ...

  6. 刷题-力扣-1738. 找出第 K 大的异或坐标值

    1738. 找出第 K 大的异或坐标值 题目链接 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/find-kth-largest-xor-co ...

  7. [LeetCode] Kth Largest Element in an Array 数组中第k大的数字

    Find the kth largest element in an unsorted array. Note that it is the kth largest element in the so ...

  8. POJ2985 The k-th Largest Group[树状数组求第k大值+并查集||treap+并查集]

    The k-th Largest Group Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8807   Accepted ...

  9. 区间第K大(一)

    Problem: 给定无序序列S:[b, e),求S中第K大的元素. Solution 1.裸排序 2.现将区间均分成两段,S1, S2,对S1,S2分别排序,然后

随机推荐

  1. git学习(三):git暂存区

    回顾之前学过的命令: git init // 初始化一个项目 git add // 将文件交给工作区 git commit // 提交修改 查看提交日志: git log // 查看提交日志 git ...

  2. Jackson2.1.4 序列化对象时,过滤null的属性 empty的属性 default的属性

    在进行序列化如何过滤为null的属性,empty的属性,或者default的属性. 一.全局注册 objectMapper.setSerializationInclusion(Include.ALWA ...

  3. /dev/null脚本中作用

    /dev/null设备文件只有一个作用,往它里面写任何数据都直接丢弃. 因此保证了该命令执行时屏幕上没有任何输出. 在shell中常见. command  > /dev/null   2> ...

  4. Java上的jQuery?解析HTML利器—Jsoup

    也许大家有过在java运行平台上解析html的经历,通常的方式是将HTML以XML的形式进行结点解析,调用java本身的xml解析类库.这样的方式很容易理解并且很方便,但习惯用jQuery的各位是否在 ...

  5. hive & hive beeline常用参数

    Hive 1参数如下: usage: hive -d,--define <key=value> Variable substitution to apply to Hive command ...

  6. BF + KMP + BM 字符串搜索算法

    BF #include <stdio.h> #include <string.h> int simplicity(char *s, char *t, int pos); int ...

  7. rails中path、url路径解析,routes信息,form_for剖析,link_to示例,路由实例说明

    原创,转载请注明http://www.cnblogs.com/juandx/p/3963023.html  rails中path.url路径解析,routes信息,form_for剖析,link_to ...

  8. 关于Cocos2d-x中GameController的定义

    GameController类是继承自Scheduler(有计时功能)或者Ref(可自动释放)的用户自己定义的一个控制器 如果一个场景有多个层的话,要用GameController,一个GameCon ...

  9. chrome浏览器插件推荐——Vimium 篇

    Vimium 是chrome底下的一个插件,所有chrome浏览器或者是基于chrome内核的浏览器(比如我用的Vivaldi)都可以使用这个插件.它提供了大量快捷键来方便键盘党浏览网页.目前在Chr ...

  10. c# winform 自动关闭messagebox 模拟回车

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...