POJ1037 -----A decorative fence
来源:互联网 发布:砧板 知乎 编辑:程序博客网 时间:2024/05/21 00:18
题目链接: A decorative fence
题意:考虑[1, 2, ..., n]的n个数的排列a1, a2, a3, ..., an. 定义合法的排列为满足: 对任意的ai, a(i-1) > ai && a(i + 1) > ai 或者a(i - 1) < ai && a(i + 1) < ai. 对于所有的合法排列, 定义顺序关系为字典序,即: 排列p1={a1, a2, .., ai, .., an}, p2 = {b1, b2, .., bi, ... bn}. 若存在1 <= i <= n使得任意的1<= k < i, ak == bk, 且 ai < bi, 则排列p1 < p2.
现在, 输入1<= n <= 20, 以及询问第m小的排列.(m 保证不超过合法排列数).
solution:来自黑书p257
首先考虑求以a1, a2, .., ak 为前k个的所有合法子数列中第num小的子数列, 则只需从1到n枚举a(k+1), 直到统计的子数列个数不小于num为止. 即: num >= sum(1 -> a(k + 1)所有合法排列数). 当然这里的合法是指如果ak > a(k + 1), 则a(k + 1) < a(k + 2). 反之亦然, 即保证数列的起伏合法性. 因而问题转化为求给定n个不同的数的排列, 并且确定第一个数为T, 以及排列的前两个数为递增或递减的所有合法排列数.
设
opt[n][t][0]: 表示考虑长度为n时, 第一个数为第t大的数, 前两个数递增的合法子序列个数.
opt[n][t][1]: .....................................................................................递减...........................
则有dp递推式
opt[n][t][0] = sum(k: t -> n -1) opt[n - 1][k][1];
opt[n][t][1] = sum(k: 1-> t - 1) opt[n - 1][k][0];
边界条件: opt[2][1][0] = opt[2][2][1] = 0;
求出opt数组之后, 接下来从前往后确定所要求的第m小的数列a1, a2, ..., an的每一个.
大概思路是. 每次从小到大(1-> n)枚举(没有出现过的)当前的ai, 直到累加的排列数不小于当前要求的第m小的. 我们即可确定ai就是要求的排列中的一个数. 然后更新m减去刚扫描过的不已ai为结尾的所有排列数. 作为下一次计算的参考.
注意一点: 枚举累加排列数的时候, 优先考虑当前值确定下递减的数列. 如果递减的数列数目和小于m,继续枚举当前值下的递增的数列.
这样当计算完a1之后, 我们就能够一次确定后面的数列的增减状态(必定是交替的).
哈哈, 说起来有点啰嗦, 不过当你理解思路之后很简单就知道怎么通过dp得到的opt数组计算解序列了.
代码:
#include <iostream>#include <cstdio>#include <memory.h>using namespace std;typedef __int64 LL;const int N = 25;LL opt[N][N][2];void dp(int n){ //0: up, 1: down memset(opt, 0, sizeof(opt)); opt[2][1][0] = opt[2][2][1] = 1; for(int i = 3; i <= n; i ++){ for(int j = 1; j < i; j ++){ for(int k = j; k < i; k ++) opt[i][j][0] += opt[i - 1][k][1]; } for(int j = 2; j <= i; j ++){ for(int k = 1; k < j; k ++) opt[i][j][1] += opt[i - 1][k][0]; } }}void solve(int n, LL m){ int rs[N]; bool used[N]; LL res = m; memset(used, false, sizeof(used)); int flag = -1, u; for(int i = 1; i < n; i ++){ if(i != 1){ int left, right; if(flag == 0){ left = u; right = n; }else{ left = 1; right = u; } flag = 1 - flag; int ind[N]; int cnt = 0; for(int j = 1; j <= n; j ++){ if(! used[j]) ind[j] = ++ cnt; } for(int j = left; j <= right; j ++){ if(! used[j]){ res -= opt[n - i + 1][ind[j]][flag]; if(res <= 0){ res += opt[n - i + 1][ind[j]][flag]; u = j; break; } } } used[u] = true; rs[i] = u; continue; } for(int j = 1; j <= n; j ++) if(! used[j]){ res -= opt[n - i + 1][j][1]; if(res <= 0){ flag = 1; res += opt[n - i + 1][j][1]; u = j; break; } res -= opt[n - i + 1][j][0]; if(res <= 0){ flag = 0; u = j; res += opt[n - i + 1][j][0]; break; } } rs[i] = u; used[u] = true; } for(int i = 1; i <= n; i ++) if(! used[i]){ rs[n] = i; break; } for(int i = 1; i <= n; i ++){ if(i != 1) printf(" "); printf("%d", rs[i]); } printf("\n");}int main(){ int t; scanf("%d", &t); while(t --){ int n; LL m; scanf("%d%I64d", &n, &m); dp(n); solve(n, m); } return 0;}
有问题欢迎交流 !
- POJ1037 A decorative fence
- POJ1037 -----A decorative fence
- POJ1037:A decorative fence(DP)
- POJ1037 A decorative fence 【动态规划】
- Poj1037 A decorative fence(DP好题)
- POJ1037————A decorative fence(动态规划)
- 1037 A decorative fence
- A decorative fence
- poj 1037 A decorative fence
- POJ 1037 A decorative fence
- POJ 1037 A decorative fence
- poj 1037 A decorative fence
- poj 1037 A decorative fence
- pku 1037 A decorative fence(DP,划分)
- 动态规划 POJ 1037 A decorative fence
- dp+计数 poj-1037-A decorative fence
- poj——A decorative fence
- POJ 1037 A decorative fence 动态规划
- TEXTOUT颜色和透明背景
- XML-Schema
- leetcode: Jump Game II
- ios UIAlertView使用全解
- 剑指offer面试题35
- POJ1037 -----A decorative fence
- 几种软件开发模型优缺点及其适用范围
- KMP字符串模式匹配详解
- 3Sum
- Python2.7 + PyQt + Eric4搭建Python GUI可视化开发环境
- C++赋值运算符重载
- freemarker四种变量
- cas client入门之三:单点登出
- [LeetCode24]Swap Nodes in Pairs