这一题是最近在看Coursera的《算法与设计》的公开课时看到的一道较难的DP例题,之所以写下来,一方面是因为DP的状态我想了很久才想明白,所以借此记录,另一方面是看到这一题有运用到 排列计数 的方法,虽然排列计数的思路简单,但却是算法中一个数学优化的点睛之笔。

  


  Poj1037  A decorative fence

  题意:有K组数据(1~100),每组数据给出总木棒数N(1~20)和一个排列数C(64位整型范围内),N个木棒长度各异,按照以下条件排列,并将所有可能结果进行字典序排序
  1.每一个木棒两侧木棒的长度都比该木棒或者(除该木棒在两端处外)

  2.木棒由小到大进行排序,完成1中排列后得到的排列即为结果,将所有结果进行字典序排序。

  

  现在求总木棒数为N时,排列数为C的结果。

  大致思路:利用动态规划构造排列状态打出1~20的排列数表,然后根据排列计数的原理找到排列数为C的排列用数组存储并输出。

    构造三维DP数组:dp[n][i][2]-n木棒下,最新插入的第 i 短木棒的可能方案数

        数组第三维具体表述:dp[n][i][DOWN]:第 i 短木棒以下降状态插入 |  dp[n][i][UP]:第 i 短木棒以上升状态插入

    构建三维DP的状态转移方程   dp[n][i][UP] = ∑(dp[n-1][k][DOWN]) (k = 1,2...i-1)  //所有n-1木棒时的下降状态之和-得到n木棒时的上升状态DP值

                  dp[n][i][DOWN] = ∑(dp[n-1][k][UP]) (k = i,i+1...n-1)   //所有n-1木棒时的上升状态之和-得到n木棒时的下降状态DP值


  排列计数:

      这一题中如果我们已经知道n个木棒的排列数,我们应该如何去求第C个排列的状态呢?

    难道我们要列出所有的排列状态,然后排序后去找吗,显然这是一种很愚蠢的做法,不仅代码冗长,而且耗时较长,所以这里需要我们进行查找排列数的优化。

   例子:

    举个例子,如果我们知道1!,2!,3!,4!...的值,现在求1~5的全排列中第41个排列数是多少该怎么求呢?

    其实我们可以简单想想如果第一位数是1的话,后面还有2~5总计4个数的全排列,因此首位是1的排列数有4! = 24种方案,24<41,因此首位一定不是1,

    现在首位为1的情况要排除掉,我们首位从2开始,现在剩余要找到的排列数是41-24 = 17了,而首位为2的排列数也是24种,24>17,因此首位一定是2了,

    首位确定了,我们就可以找第二位了,首先从1开始,后面还有三位数排列,因此排列数共3! = 6,6<17,因此第二位一定不是1了,

    所以我们第二位从没有确定的3开始,现在要找寻的排列数是17-6=11....

   以此类推,我们就可以找到第41种排列情况是24513,这样的最坏时间度为O(n2)

   那么这一题也可以采用类似的简单排列计数算法

   最终 Code 如下:

    

 //Memory:180K Time:0 Ms
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std; #define MAX 21 enum State{
DOWN, //下降状态
UP, //上升状态
}; __int64 dp[MAX][MAX][]; //所有状态
int permut[MAX]; //答案排列-permutation
int v[MAX]; void DP(int n) //初始DP-dp[i][j][]-为bar共 i 个时,最新insert木棒 j 的总情况数
{
dp[][][DOWN] = dp[][][UP] = ;
for (int i = ; i <= n; i++) //现有bar数
for (int j = ; j <= i; j++) //最新insert的bar M (第j短)
{
for (int k = j; k < i; k++) //+all可达此上升态的上一个状态(下降)DP值(k >= j)
dp[i][j][UP] += dp[i - ][k][DOWN];
for (int k = ; k < j; k++) //+all可达此下降态的上一个状态(上升)DP值(k < j)
dp[i][j][DOWN] += dp[i - ][k][UP];
}
return;
} void Find_permutation(int n, __int64 c)
{
memset(v, , sizeof(v));
memset(permut, , sizeof(permut));
for (int i = ; i <= n; i++)
{
__int64 skip = ; //跳过方案数
int No = ;
for (int cur = ; cur <= n; cur++) //第cur短的bar
{
if (!v[cur])
{
No++; //cur在剩余木棒中第No短
if (i == )
skip = dp[n][No][UP] + dp[n][No][DOWN]; //No==1
else
{
//题意条件+排列计数知识
if (cur > permut[i - ] && (i == || permut[i - ] > permut[i - ]))
skip = dp[n-i+][No][DOWN]; //前一所有下降状态-达到当前上升状态
else if (cur < permut[i - ] && (i == || permut[i - ] < permut[i - ]))
skip = dp[n-i+][No][UP]; //前一所有上升状态-达到当前下降状态
}
if (skip >= c)
{
v[cur] = ;
permut[i] = cur;
break;
}
else
c -= skip;
}
}
}
/* PRINT */
for (int i = ; i <= n; i++)
printf("%d ", permut[i]);
printf("\n");
} int main()
{
int T, n;
__int64 c; DP(); scanf("%d", &T);
while (T--)
{
scanf("%d%I64d", &n, &c); Find_permutation(n, c);
} return ;
}

小墨原创


ACM/ICPC 之 DP-浅谈“排列计数” (POJ1037)的更多相关文章

  1. ACM/ICPC 之 DP解有规律的最短路问题(POJ3377)

    //POJ3377 //DP解法-解有规律的最短路问题 //Time:1157Ms Memory:12440K #include<iostream> #include<cstring ...

  2. ACM/ICPC 之 最长公共子序列计数及其回溯算法(51Nod-1006(最长公共子序列))

    这道题被51Nod定为基础题(这要求有点高啊),我感觉应该可以算作一级或者二级题目,主要原因不是动态规划的状态转移方程的问题,而是需要理解最后的回溯算法. 题目大意:找到两个字符串中最长的子序列,子序 ...

  3. ACM/ICPC 之 DP进阶(51Nod-1371(填数字))

    原题链接:填数字 顺便推荐一下,偶然看到这个OJ,发现社区运营做得很赞,而且交互和编译环境都很赞(可以编译包括Python,Ruby,Js在内的脚本语言,也可以编译新标准的C/C++11,甚至包括Go ...

  4. 数位dp浅谈(hdu3555)

    数位dp简介: 数位dp常用于求区间内某些特殊(常关于数字各个数位上的值)数字(比如要求数字含62,49): 常用解法: 数位dp常用记忆化搜索或递推来实现: 由于记忆化搜索比较好写再加上博主比较蒟, ...

  5. 2016 ACM/ICPC Asia Regional Shenyang Online 1009/HDU 5900 区间dp

    QSC and Master Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...

  6. 2016 ACM/ICPC Asia Regional Shenyang Online 1007/HDU 5898 数位dp

    odd-even number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)T ...

  7. BZOJ4517 Sdoi2016 排列计数 【DP+组合计数】*

    BZOJ4517 Sdoi2016 排列计数 Description 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 ...

  8. [ZJOI2010]排列计数 (组合计数/dp)

    [ZJOI2010]排列计数 题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有 ...

  9. HDU 5000 2014 ACM/ICPC Asia Regional Anshan Online DP

    Clone Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/65536K (Java/Other) Total Submiss ...

随机推荐

  1. Xshell中文版使用说明/下载

    个人本来一直用SecureCRT的这几天朋友推荐用xshell就尝试下载了一个结果发现这个比SecureCRT和puttytel好使多了切更能更完善强大特此推荐附带注册码一枚可装999台机器 1309 ...

  2. MD5 (摘要加密)

    MD5 约定 同样的密码,同样的加密算法,每次加密的结果是不一样 密码方案 方案一:直接 MD5 pwd = pwd.md5String; 非常不安全 方案二 MD5 + 盐 pwd = [pwd s ...

  3. VisualStudio基本使用(2)-使用VS2013来编译C语言程序

    切换到C语言程序所有目录,执行cl test.c

  4. 【转】【整理】将Linux脚本中的正常输出,警告,错误等信息输出到文件中

     本文来自:http://blog.csdn.net/woshinia/article/details/18040063   很早以前  编译的时候 就在用 2>&1,但是一直没有生成一 ...

  5. [慢查优化]联表查询注意谁是驱动表 & 你搞不清楚谁join谁更好时请放手让mysql自行判定

    写在前面的话: 不要求每个人一定理解 联表查询(join/left join/inner join等)时的mysql运算过程: 不要求每个人一定知道线上(现在或未来)哪张表数据量大,哪张表数据量小: ...

  6. 时间处理工具类DateUtils

    public class DateUtils {         public static final String                            SHORT_DATE    ...

  7. Codeforces Round #202 (Div. 2) A,B

    A. Cinema Line time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...

  8. Eclipse经常报Unhandled event loop exception的原因

    在公司的电脑上,Eclipse经常报Unhandled event loop exception 错误,非常频繁,通过搜索发现是因为电脑上安装了百度杀毒导致的.... 无语 另外 teamviewer ...

  9. torch 入门

    torch 入门1.安装环境我的环境mac book pro 集成显卡 Intel Iris不能用 cunn 模块,因为显卡不支持 CUDA2.安装步骤: 官方文档 (1).git clone htt ...

  10. 搭建 Windows Server 2012 FTP 服务器

    在Server2012打开 服务器管理器,选择 添加角色与功能,添加Web服务下的FTP服务器 单击安装 我们现在C盘创建一个名字为FTP的文件夹,里面创建一个ftp的文件,做测试用,如图 打开服务器 ...