【洛谷】P1088 火星人(模拟、全排列)

本文深入解析了全排列算法,通过具体实例介绍了如何使用交换法获取下一个全排列。首先,从右往左找到第一个左侧数小于右侧数的位置;其次,继续从右往左找到第一个大于该数的数并进行交换;最后,确保交换后序列的升序排列。

题目:https://www.luogu.org/problem/P1088

标签:模拟、排序、全排列、搜索

【题解】

通过交换来得到下个全排列,交换的前提是交换左边的数要小于交换右边的数。

先从右往左找到左边小于相邻右边的数的位置k,再从右往左找到大于该数的数(可能会有多个),然后交换这两个数,最后要让k位置后的数排列成升序。

【代码】

#include <iostream>
#include <algorithm>
using namespace std;
int m, n, k;	//k表示当前选取到第几个数
int a[10005];

void permutation()
{
	for (k = n - 1; k >= 1; k--)//找到最后可增加的位,即定位 
	{
		if (a[k] < a[k + 1])
			break;
	}
	for (int i = n; i > k; i--)//找到最小可增加的数字 
	{
		if (a[i] > a[k]) 
        {
			swap(a[i], a[k]);	//交换
			break;
		}
	}
	sort(a + k + 1, a + n + 1);	//让下标k后面的序列升序
}

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
		
	while (m--)
		permutation();	//一次排列
		
	for (int i = 1; i <= n; i++)
		cout << a[i] << " ";

	cout << endl;
	return 0;
}
洛谷P1088火星人题目的描述为:输入火星人手指的数目 `N`(1 ≤ N ≤ 10000),要加上去的小整数 `M`(1 ≤ M ≤ 100),以及 1 到 `N` 这 `N` 个整数的一个排列表示火星人手指的排列顺序,输出改变后的火星人手指的排列顺序,每两个相邻的数中间用一个空格分开,不能有多余的空格 [^2]。 在C语言中,可以利用 `next_permutation` 函数的思想来实现这个功能,不过C标准库中没有 `next_permutation` 函数,需要手动实现。以下是实现代码: ```c #include <stdio.h> // 交换两个整数的值 void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } // 反转数组中从start到end的元素 void reverse(int arr[], int start, int end) { while (start < end) { swap(&arr[start], &arr[end]); start++; end--; } } // 生成下一个排列 int next_permutation(int arr[], int n) { int i = n - 2; // 从后往前找到第一个相邻元素对 (i, i+1) 使得 arr[i] < arr[i+1] while (i >= 0 && arr[i] >= arr[i + 1]) { i--; } if (i < 0) { return 0; // 没有下一个排列 } int j = n - 1; // 从后往前找到第一个元素 arr[j] 使得 arr[j] > arr[i] while (arr[j] <= arr[i]) { j--; } // 交换 arr[i] 和 arr[j] swap(&arr[i], &arr[j]); // 反转从 i+1 到 n-1 的元素 reverse(arr, i + 1, n - 1); return 1; // 成功生成下一个排列 } int main() { int n, m; int arr[10010]; // 输入 n 和 m scanf("%d %d", &n, &m); // 输入初始排列 for (int i = 0; i < n; i++) { scanf("%d", &arr[i]); } // 生成下 m 个排列 for (int i = 0; i < m; i++) { next_permutation(arr, n); } // 输出结果 for (int i = 0; i < n; i++) { printf("%d", arr[i]); if (i < n - 1) { printf(" "); } } printf("\n"); return 0; } ``` 上述代码首先实现了 `next_permutation` 函数,用于生成数组的下一个排列。在 `main` 函数中,读取输入的 `n`、`m` 和初始排列,然后调用 `next_permutation` 函数 `m` 次,最后输出结果。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值