第K个幸运排列 (深搜加暴力)

比得喜欢幸运数字。这里所说的幸运数字是由47组成的正整数。比如,整数477444是幸运数字,而517467就不是。

 

一天比得梦到由数字1n组成的第K个字典序排列。要求计算在这个排列中有多少个幸运数所在的位置的编号也是幸运数。

 

举例如下:

比如排列[1234],其中4为幸运数,它所在的位置的下标为4(幸运数),所在此排列中,结果为1.

又如,[1243]4所在位置为33不是幸运数,所以,结果为0.


样例解释:

排列是由n个元素组成的一个序列,在这个序列中,整数1n都要有且仅出现一次。

在排列中,第i个元素定义为 aiai (1in)。

假定有排列a,和排列b。长度均为n。即都是由1n组成。如果存在i1in)和对于任意j1ji)使得 ai  biai < bi 且 aj  bjaj = bj 。我们就说,a的字典序比b的字典序小。

1n组成的所有排列进行字典序升序排列。然后取其中的第K个排列,就是第K个字典序排列。

 

在样例中,最终排列如下:

1 2 3 4 6 7 5

只有第4个位置是幸运数。


Input 单组测试数据
共一行,包含两个整数n和k(1≤n,k≤10^9)表示排列中的元素个数,和第K个字典序排列。 Output 如果k超过出了1到n所有排列数的种数,那么输出“-1”(没有引号)。
否则,输出题目要求结果。 Sample Input
7 4
Sample Output
1
 
 


这题是这学期最后一次模拟赛的A题:

题目大意就是说1到n的第m个全排列中,求下标和数值都是幸运数的个数,其中数字全是4或者7的数叫做幸运数。

刚开始想了好久,其实跟上次那题全排列类似,前面的数不受影响,可以用深搜判断,因为下标就等于数值,不需要一一分类讨论,第ii项开始,会因为第m个全排列而发生变化,所以要用for循环遍历一遍,当下标和数组的值都是幸运数,再让ans++。

总体思路挺简单的,判断是否幸运数的函数也不难写,中间的全排列产生如果看不懂,可以参考我以前写的博客。

把答案分为两部分,第一部分为1到ii-1不会变的数,第二部分为ii到n会变的数,一个深搜,一个遍历。

不多说,直接上代码:


#include<iostream>  
using namespace std;
long long n, m, ans, a[100], f[100], g[100], n1;
long long pd(long long t)
{//a数组用于判断是否为幸运数
	long long n1 = 0;
	while (t)
	{
		n1++;
		a[n1] = t % 10;
		t /= 10;
		if (a[n1] != 4 && a[n1] != 7)return 0;
	}
	return 1;
}
void dfs(long long x)//dfs深搜
{
	if (x>n1)return;
	if (x)ans++;
	dfs(x * 10 + 4);
	dfs(x * 10 + 7);
}
int main(){
	long long i, ii, j, t, k, s, la;
	scanf("%lld%lld", &n, &m);//1到n的第k个排列
	t = 1;
	for (i = 2; i <= n; i++)   //这里计算n的阶乘
	{
		if (t>m)break;     //用于计算第m种排列会影响最后多少个数字
		t *= i;
		la = i;
	}
	if (t<m)//如果n的阶乘比m小,输出-1
	{
		printf("-1");
		return 0;
	}
	ii = la;//ii是受影响的最后数字的总排列个数
	k = t;//k是受影响的最后数字的总排列阶乘
	for (i = 1; i <= ii; i++)
	{
		g[i] = 1;
	}
	for (i = n - ii + 1; i <= n; i++)//最后ii+1个数遍历一遍
	{
		k /= (ii - (i - (n - ii + 1)));//相当于n!除以n
		s = 0;
		for (j = 1; j <= ii; j++)
		if (g[j])
		{
			s++;
			if (s*k >= m)
			{
				m -= (s - 1)*k;
				g[j] = 0;
				f[i - (n - ii)] = j + (n - ii);
				break;
			}
		}
	}//实现第m个全排列
	//全排列产生方法不会的看这里https://blog.csdn.net/qq_41464123/article/details/80584899
	n1 = n - ii;			//n1代表全排列中前面不变的数的个数
	dfs(0);					//0到n1是不会变的 所以直接暴力模拟
	for (i = 1; i <= ii; i++)//显然,ii是第m个排列影响到的最后几位的位数,
		//会根据m的排列而变化  所以要判断下标和数值
	if (pd(f[i]) && pd(n - ii + i))ans++;
	printf("%lld\n", ans);//最后输出两部分的和
}
有疑问或者侵权联系[email protected]

猜你喜欢

转载自blog.csdn.net/qq_41464123/article/details/80639998