题目大意

给定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. C语言 · 输出日历

    算法提高 输出日历   时间限制:1.0s   内存限制:512.0MB      按照下述格式打印2006年12月日历: Calendar 2006-12---------------------- ...

  2. kernel 3.2.0 上加入自己的板级文件

    第一步.复制自己的参考的板级文件 cp arch/arm/mach-omap2/board-am335xevm.c arch/arm/mach-omap2/board-aplex_ECM_5412.c ...

  3. iOS边练边学--transform的简单介绍并且用transform实现键盘处理

    一.transform:形变属性,能完成功能:平移,缩放,旋转 <平移> // 根据给的移动距离平移 self.tempView.transform = CGAffineTransform ...

  4. 隐藏的Swiper显示后无法获取正确的宽度和高度

    今天在使用swiper的时候,元素默认是显示的时候没毛病,但是默认是隐藏的状态,再显示的时候发现滑动的时候宽度计算有误,如下图所示: 正确的显示如下: 隐藏的元素再次显示如下: 宽度计算有误 解决方案 ...

  5. jquery动态分页

    最近一直研究jquery的分页效果,刚刚弄好了一个,拿出来与大家分享.分页效果与时光网的差不多. 网址:http://www.mtime.com/movie/news/all/ 先在aspx页面放置一 ...

  6. MFC 无边框窗体实现用鼠标拖动窗体边缘实现窗体大小变化

    无边框窗体如何实现用鼠标拖动窗体边缘实现窗体大小变动呢?下面介绍一种方法,通过以下几个步骤即可实现: 1.实现WM_NCHITTEST消息,实现四条边框的模拟 2.实现WM_NCLBUTTONDOWN ...

  7. am335x hid-multitouch.c

    am335x在使用电容屏,需要加载hid-multitouch.ko模块 由下面文件生成 kernel/drivers/hid/hid-multitouch.c 内核中编译模块 make module ...

  8. PHP wamp server问题

    只能说各种不顺,天热人烦! 问题一.wampapache服务不能启动之VCRUNTIME140.DLL文件丢失. 这个是缺少VC运行库,查找并安装相应的文件即可. 问题二.wampapche服务不能启 ...

  9. tiny210V2开发板hdmi输出到10.1寸LCD,无图像

    tiny210V2开发板hdmi输出到10.1寸LCD,无图像... 用tiny210V2开发板的HDMI接口输出到的10.1寸LCD,LCD无任何现象.说明一下我的情况,我的10.1寸屏LCD是HD ...

  10. jquery-创建元素和添加子元素

    一.创建新元素 1.使用$函数创建新元素 var $newElement=$('<div><p>段落</p></div>');//创建元素,返回jQue ...