参考:
一、求字典序的第i个排列
参见: http://jay23jack.blog.163.com/blog/static/317951942009130215813/
暴力枚举可以很轻松地得到符合的排列,事实上不妨ms写一个,来产生更强的样例。当然,答案不会是暴力,而是一种很常见的方法,可以说是这种求指定编号问题的通法吧!
直接一位一位枚举答案!从前到后枚举求得每一位:枚举一位时,计算在这样的前缀下,后面的总的排列数。如果严格小于总编号,则该位偏小,换更大的数,同时更新总编号;若大于等于,则该位恰好,枚举下一位,总编号不用更新。
先抛开此题,用最简单的全排列问题来说明这种方法。如1,2,3,4的全排列,共有4!种,求第10个的排列是?
先试首位是1,后234有3!=6种<10,说明1偏小,转化成以2开头的第(10-6=4)个排列,而3!=6 >= 4,说明首位恰是2。
第二位先试1(1没用过),后面2!=2个<4,1偏小,换成3(2用过了)为第二位,总编号也再减去2!,剩下2了。而此时2!>=2,说明第二位恰好是3。
第三位先试1,但后面1!<2,因此改用4。末位则是1了。
这样得出,第10个排列是2-3-4-1。(从1计起)
这种方法的核心在于求,给定前缀下后面总的排列数。全排列问题较易求。同时还需记录前面用过的数字。
再回到此题,同是这样来求。但求后面总的排列数时,用到了递推,或者说dp,不难的方程。
搞懂了后,去poj上做题,先是WA,试过书上的样例后,发现忘记判断“波浪形”了。于是慨叹,强大的数据是成功调试的有力保证!以后还是要不厌其烦地写一些样例来才行啊!
Sam:在求字典序的第i个排列时,关键是求得每种特定前缀子串的可能排列数目,如上面例子中,要求出字典序第i大,需要知道以下序列的个数
(*代表可以使任何可能数字):
1 * * *
1 2 * *
1 2 3 *
1 2 4 *
1 3 * *
1 3 2 *
1 3 4 *
1 4 * *
1 4 2 *
1 4 3 *
2 * * *
2 1 * *
2 1 3 *
2 1 4 *
2 3 * *
2 3 1 *
2 3 4 *
2 4 * *
2 4 1 *
2 4 3 *
二、DP
参见: http://hi.baidu.com/superlong/blog/item/783eb18f9aa063e7f11f36ba.html
这个题目的意思很纠结,是给你一个序列的长度,然后给你一个排列规则,让你输出字典序第m的排列。这个排列规则是这样的:对于排列中的每一个数都有:(a[i]>a[i-1] && a[i]>a[i+1]) || (a[i]<a[i-1] && a[i]<a[i+1]),这个题是用dp加上所谓数学方法(黑书p257),不过另外的dp方程也是可以的,即 DP.down[i][l] 表示以i开始,长度为l,并且初始初始状态是向下放置的(即第二个数小于第一个数),同样DP.up[i][l]就不解释了,那么就有
DP.down[i][l] = sigema{DP.up[[k][l-1](1<=k<i)}
DP.up[i][l] = sigema{DP.down[l+1-i][l].
这样我们可以得到任意状态的排列数,然后就是麻烦的输出了。
-------------------------------------------------------------------------------------------------------------------------------------
代码:
//poj 1037 A decorative fence 经典的DP+计数
#include <iostream>
using namespace std;
int n;
__int64 m;
__int64 dp[21][21][2];
int mark[21];//mark[i]=1表示长度为i的plank已经使用
int main()
{
//1. DP
dp[1][1][0]=dp[1][1][1]=1;
for (int len=2;len<=20;len++){
for (int i=1;i<=len;i++)
{
for (int j=1;j<i;j++) dp[len][i][0]+=dp[len-1][j][1];
for (int j=i;j<len;j++) dp[len][i][1]+=dp[len-1][j][0];//?为什么这里不是n呢???
}
}
//2. 求字典序第i个排列
int t;
scanf("%d",&t);
while (t--)
{
scanf("%d%I64d",&n,&m);
int l=1,r=n,j,finded=0;
memset(mark,0,sizeof(mark));
for (int i=1;i<=n && !finded;i++)
{
for (j=0;j<2;j++)
{
m-=dp[n][i][j];
if (m<=0) {
m+=dp[n][i][j]; //第n位是i,跳出这两个for后需要寻找前缀是1的字典序第m个序列
mark[i]=1;
printf("%d",i);
if (j==0) l=1,r=i-1;
else l=i+1,r=n;
finded=1;
break;
}
}
}
j^=1;
for (int len=n-1;len>=1;len--) //依次找到第(n-1)~1位
{
int num=0;
for (int i=1;i<l;i++) if (!mark[i]) ++num;//本位置剩余可以尝试的数字个数: num???
for (int i=l;i<=r;i++)
if (!mark[i])
{
num++;
m-=dp[len][num][j];
if (m<=0){
m+=dp[len][num][j];
mark[i]=1;
printf(" %d",i);
if (j==0) l=1,r=i-1;
else l=i+1,r=n;
j^=1;
break;
}
}
}
printf("\n");
}
system("pause");
return 0;
}
分享到:
相关推荐
标题“POJ3253-POJ3253-Fence Repair【STL优先队列】”指的是一个在线编程竞赛题目,源自北京大学的在线判题系统POJ(Problem Online Judge)。该题目要求参赛者使用C++编程语言解决特定的问题,并且在解决方案中...
【标题】"POJ2002-Squares"是一个经典的计算机编程题目,源自北京大学的在线判题系统(POJ,即PKU Online Judge)。这个题目主要涉及到算法设计和实现,尤其是数学和动态规划方面的知识。 【描述】"解题报告+AC代码...
【标题】"POJ1015-Jury Compromise" 是一个编程竞赛题目,主要涉及的是动态规划(Dynamic Programming, 简称DP)的算法应用。动态规划是一种解决复杂问题的有效方法,它通过将问题分解成子问题,并存储子问题的解来...
【标题】"POJ1426-Find The Multiple【BFS+同余模】"是一道来源于北京大学在线编程平台POJ的算法题目,主要涉及到了广度优先搜索(BFS)与同余模运算的知识点。这道题目要求解决的是寻找一个整数的倍数问题,可能...
【标题】"POJ1005-I Think I Need a Houseboat" 是一个编程竞赛题目,源自北京大学的在线评测系统POJ(Problem Set of Peking University)。这个题目旨在考验参赛者的算法设计和实现能力,主要涉及数据结构和动态...
【标题】"POJ1584-A Round Peg in a Ground Hole" 是一道来自北京大学在线判题系统POJ(Problem Set)的经典算法题目。这道题目主要涉及的是几何计算和位运算的应用,属于计算机科学中的基础算法问题。 【描述】...
输入是一组字符串,输出则是这些字符串按字典序排序后的结果,但需要注意的是,如果两个字符串只在最后一个字符上有区别,那么较短的那个字符串应该排在前面。这就需要我们借助Trie树来快速地进行查找和排序。 Trie...
【标题】"POJ3020-Antenna Placement"是一个经典的计算机编程竞赛题目,源自北京大学的在线评测系统POJ(PKU Online Judge)。这个题目挑战了参赛者在算法设计和实现上的能力。 【描述】"解题报告+AC代码"意味着这...
这里,dp[i-1][j]表示不购买第i种裤子的最小花费,而dp[i][j-p[i]] + m[i]表示购买第i种裤子的最小花费。最终,我们寻找dp[n][k],即购买k种不同裤子的最低总花费。 【标签】"POJ 3080 Blue Jeans" 指明了这道题目...
通常,排序可以按照字典序(升序或降序)进行,或者根据特定的生物学规则,如碱基对的配对规则(A-T,C-G)。 解题报告通常会包含以下几个部分: 1. 题目理解:解析题目要求,明确输入和输出格式,以及排序的规则。...
【标题】"POJ2299-Ultra-QuickSort"是北京大学在线判题系统POJ上的一道编程题目,其主要涉及的算法是快速排序(Ultra-QuickSort)。快速排序是一种高效的排序算法,由C.A.R. Hoare在1960年提出。它的基本思想是采用...
《POJ 1000 - 2000 部分题目 官方分类》 编程竞赛,特别是在线判题系统(如POJ,即Problem Online Judge)中的题目,是提升编程技能和算法理解的重要途径。POJ 1000 - 2000 是一个涵盖广泛的题目区间,包含了大量的...
在这个问题中,我们可以定义一个二维数组dp[i][j]表示前i个文件用j台碎纸机处理的最短时间。 2. **状态转移方程**:状态转移方程是动态规划的核心,描述了如何从一个状态转移到另一个状态。对于此题,可能的状态...
这个题目主要涉及到图论中的一个重要算法——普里姆(Prim)算法。 【描述】"北大POJ1258-Agri-Net【Prim】 解题报告+AC代码" 暗示了这是一个关于如何解决该编程题目的指南,包括了解题思路的阐述和通过测试...
【标签】"POJ 1942 Paths on a Grid"标签明确了问题的来源和主题,这是POJ平台上的第1942号题目,主题是关于网格上的路径计算。 【压缩包子文件的文件名称列表】中包含的文件: 1. "POJ1942-Paths on a Grid...
【标题】"POJ1837-Balance"是一个在线编程竞赛题目,源自著名的编程练习平台POJ(Programming Online Judge)。这个题目旨在测试参赛者的算法设计和实现能力,特别是处理平衡问题的技巧。 【描述】"解题报告+AC代码...
二是第i个字符为闭括号,此时dp[i][j]取决于在前i-1个字符中是否有可用的开括号进行配对,即dp[i][j] = ∑dp[k][j-1],其中k从0到i-1遍历,表示从字符串开始到当前位置的所有可能的开括号对。 AC代码通常是指通过了...
【标题】"POJ1010-STAMPS"是一个编程题目,来源于北京大学的在线判题系统POJ(Problem Set of Peking University),这是一处训练程序员算法技能和编程能力的平台。该题目旨在考察参赛者对动态规划或贪心算法的理解...
标题“POJ3414-Pots”是一个编程竞赛题目,源自北京大学的在线判题系统POJ(Problem Online Judge)。这个题目主要考察的是算法设计和实现能力,通常涉及计算机科学中的数据结构和算法知识。 解题报告是参赛者在...
【标题】"POJ2488-A Knight's Journey【骑士游历】" 【知识点解析】 本题是来自北京大学在线编程平台POJ的一道经典算法题——“骑士游历”。题目要求解决的是一个典型的图论问题,即在国际象棋棋盘上,骑士能否...