P2524 Uim的情人节礼物·其之弐 [洛谷]

1.题目

题目链接

题目描述

Uim成功地按照顺序将礼物送到了N个妹子的手里并维持她们的和谐。

Uim现在想知道,他最终选择的顺序是所有给N个妹子送礼顺序中、字典序第几小的。

输入格式

第一行一个整数N,表示有N个数。

第二行一个整数X,表示给出的排列。

输出格式

一个整数,表示是第几小的字典序。

样例输入 #1

3
231

样例输出 #1

4

提示
1<=N<=9
输入的排列没有空格

2.分析

思路:
①next_permutation
②prev_permutation
③康托展开
④dfs

3.代码

1.next_permutation()

找到该排列的下一个排列

#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>  //next_permuation
vector <int> a,b;
int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
		a.push_back(i);
	for (int i = 0; i < n; ++i)
	{
		char t;
		cin >> t;
		b.push_back(t - '0');
	}
	int cnt = 1;
	while (a != b)
	{
		next_permutation(a.begin(), a.end());
		cnt++;
	}
	cout << cnt;
	return 0;
}

2.prev_permutation()

找到该排列的上一个排列

#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>  //prev_permuation
vector <int> a;
int main()
{
	int n;
	scanf("%d", &n);
	string s;
	cin >> s;
	for (auto x : s)
		a.push_back(x - '0');
	int cnt = 1;
	while (prev_permutation(a.begin(), a.end()))
		cnt++;
	cout << cnt;
	return 0;
}

3.康托展开

百度百科:康托展开 跳转链接

//康托展开 这里只说明用法
//X = a[n] * [(n - 1)!] + a[n - 1] * [(n - 2)!] + .... + a[1] * [0!]
//X为比当前排列小的序列个数  a[i]表示i右侧比i位置小的数字个数
#include <iostream>
using namespace std;

int a[10];

int fac(int n)  //n的阶乘大小
{
	if (n == 1) return 1;
	return n * fac(n - 1);
}

int cantor(int n)   //康托展开
{
	int res = 0, cnt = 0;
	//从前向后判断
	for (int i = 1; i < n; ++i)  //最后一位不用判断,固定为0
	{
		for (int j = i + 1; j <= n; j++)
			if (a[j] < a[i])
				cnt++;
		res += cnt * fac(n-i);
		cnt = 0;  //计数器清零
	}
	return res+1; //res为比当前小的序列个数,则当前序列为第res+1小的序列
}

int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		char c;
		cin >> c;
		a[i] = c - '0';
	}
	cout << cantor(n);
	return 0;
}

4.dfs

搜索每一种排列,并每次与原序列比较

//dfs
#include <iostream>
using namespace std;

int path[10];  //记录路径
bool st[10];   //是否使用过某个数字
int n;
int a[10];  //初始序列
int cnt;
bool same_seq()   //判断是否是同一个序列
{
	++cnt;  //每次调用都要++
	for (int i = 0; i < n; ++i)
		if (path[i] != a[i])
			return false;
	return true;
}

void dfs(int u)  //u为下标
{
	if (u == n)  //走到尾了
	{
		if (same_seq()) cout << cnt;
		return;
	}

	for (int i = 1; i <= n; ++i)
	{
		if (!st[i]) //未被使用
		{
			path[u] = i;
			st[i] = true;
			dfs(u + 1);   //搜索下一个位置
			st[i] = false;   //回溯
		}
	}
}

int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; ++i)
	{
		char c;
		cin >> c;
		a[i] = c - '0';
	}
	dfs(0);
	return 0;
}

4.总结

库函数~

5.更新日志

2022.8.7

欢迎交流、讨论、指正~
不正确、不理解之处欢迎评论留言~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值