题目来源:http://acm.wust.edu.cn/problem.php?id=1074&soj=0
题目描述:火星人共有N个手指,每个手指分别代表着1-N共N个数,可以通过改变这个这N个手指的顺序来改变值的大小。但是人类想要和火星人交流,就必须通过科学家,科学家先将火星人讲的话(手指表示的数)翻译成我们能理解的语言(如火星人共3个手指,则123 132 213 231 312 321分别代表1 2 3 4 5 6),然后告诉你一个数,你把这个数和火星人讲的话加起来回给科学家,科学家再翻译成火星人的语言。本题给出火星人的手指数N、要加的数M和手指的顺序num[N],要求输出火星人收到的回话。
刚开始看到这个题目,还是有点懵逼的,但是知道它是一个全排列问题,所以赶紧的去了解了一下全排列,顺利地解决了这个问题。
本题就是要求求出num[N]经过M次排列后的结果。
对于一系列的数,比如int num[5] = {1,2,3,4,5}这个数组,要想对它进行全排列,要经过以下几个步骤:
1.判断该数组能不能进行全排列
对于一个数组来说,如果他为num[5] = {5,4,3,2,1},那么也就没有必要再去全排列了,因为他已经是最大的数字了,没有后继。所以,想要判断一系列数能不能进行全排列,判断他有没有后继(即这个数是否存在非递减的两个数),如果有(存在),那就可以进行排列。
判断是否能进行全排列的代码:
bool hasNext()
{
for( int i = N; i > 0; i--)
if( num[i] > num[i-1])
return true;
return false;
}
2.如何进行全排列(当时想这个想了挺久的==)
在确定这一系列的数有后继之后,那如何去找到它的后继呢?要明确,一个数的后继要满足两个条件:比这个数大、在比这个数大的数里面最小。
首先,我们从右往左遍历这个数组,找出一个数num[i],满足num[i]>num[i-1],然后用top将这个i记录下来(即top为极大值点),并且确定了一个要交换的数num[top-1];
接着,我们要确定第二个要交换的数, 而第二个要交换的数为num[top]-num[N]中最小的数并且这个数要大于第一个被交换的数num[top-1];
然后,交换两个数;
最后,如果交换之后,num[top]及其后面的数如果还是单调递减的,那就将其位置对调,得到最小的。
找出极大值得top并记录
for( int i = N-1; i >0; i--)
{
if( num[i] > num[i-1])
{
top = i;
break;
}
}
int mm = top;//mm为要交换数的下标
//如果top后面还有比top前面的数(也就是num[top-1)小的话,就先交换那个小的数
for( int i = top; i < N; i++)
{
if( num[i] > num[top-1] && num[i] < num[top])
mm = i;
}
void _swap( int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
_swap(&num[mm],&num[top-1]);
for(int i=0;i<=(top+N-1)/2-top;i++)
_swap(&num[i+top],&num[N-1-i]);
大概的思路就是这个样子了==
可能讲的还不是太清楚,其实对于全排列问题,用递归、c++的库函数都可以完成的。
源码:
#include
using namespace std;
const int maxn = 10000+5;
int num[maxn];
int N,M;
//个人觉得这个不写也没问题,但是为了安全,还是写着吧
int hasNext()
{
for( int i = N-1; i > 0; i--)
if( num[i] > num[i-1])
return true;
return false;
}
void _swap( int *a, int *b)
{
int m = *a;
*a = *b;
*b = m;
}
void next()
{
int top;
//从又开始遍历数组,找出右边第一个极大值,用top保存(此时也找到了第一个要交换的数num[top-1])
for( int i = N-1; i > 0; i--)
if( num[i] > num[i-1])
{
top = i;
break;
}
//找出第二个要交换的数
int mm = top;
for( int i = top; i < N; i++)
{
if( num[i] > num[top-1] && num[i] < num[top])
mm = i;
}
_swap( &num[top-1], &num[mm]);
for( int i = 0; i <= (top+N-1)/2-top; i++)
_swap( &num[i+top], &num[N-1-i]);
}
int main()
{
while( scanf("%d%d",&N,&M) == 2)
{
for( int i = 0; i < N; i++)
scanf("%d",&num[i]);
for( int i = 0; i < M; i++)
{
if( hasNext())
next();
}
printf("%d",num[0]);
for( int i = 1; i < N; i++)
printf(" %d",num[i]);
printf("\n");
}
return 0;
}
//用vector容器不用数组更容易实现呢==
用c++中的库函数
#include
#include
using namespace std;
const int maxn = 10000+5;
int num[maxn];
int main()
{
int n,m;
while( scanf("%d%d",&n,&m) == 2)
{
for( int i = 0; i < n; i++)
scanf("%d",&num[i]);
for( int i = 0; i < m; i++)
next_permutation(num,num+n);
for( int i = 0; i < n; i++)
printf(i==n-1?"%d\n":"%d ",num[i]);
}
return 0;
}