题目大意

给定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. 【WPF】两个下拉列表ComboBox的级联

    需求:两个ComboBox的级联,实现城市–小区级联. 问题:个人感觉WPF的核心应该是数据绑定这块.由于时间紧迫,粗略看Binding也是一头雾水,所以用了比较简单的方法做了两个下拉列表级联的效果: ...

  2. 利用AbstractRoutingDataSource实现动态数据源切换

    需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...

  3. C++实现 逆波兰表达式计算问题

    C++实现 逆波兰表达式计算问题 #include <iostream> #include <string> using namespace std; class Stack ...

  4. TP5 display()

    tp3.x $this->display(); tp5 return $this->fetch();

  5. sublime text2 配置

    2.使用Package Control组件安装 也可以安装package control组件,然后直接在线安装: 按Ctrl+`调出console 粘贴以下代码到底部命令行并回车:import url ...

  6. java-事务-案例

    项目结构: 数据库: /* SQLyog Ultimate v12.09 (64 bit) MySQL - 5.5.53 : Database - threadlocal ************** ...

  7. 面向移动端的轻量级神经网络模型mobilenet、ShuffleNet

    翻译: http://baijiahao.baidu.com/s?id=1565832713111936&wfr=spider&for=pc http://baijiahao.baid ...

  8. JAVA 多线程机制(二)

    主要内容 1.理解线程的并发性 2.线程的同步 3.线程的常用方法   上一章中由于线程的并发性导致了多线程的执行总是会出现一些问题..线程的并发性是程序员不可控制 的,也是不可避免的,线程的并发性往 ...

  9. jquery轻量级富文本编辑器Trumbowyg

    html: <!DOCTYPE html> <html lang="zh-cn"> <head> <meta http-equiv=&qu ...

  10. ubuntu12.04 修改登陆用户 为root

    Ubuntu 12.04默认是不允许root登录的,在登录窗口只能看到普通用户和访客登录.以普通身份登陆Ubuntu后我们需要做一些修改,普通用户登录后,修改系统配置文件需要切换到超级用户模式,在终端 ...