关于排列(poj1833)问题

对于这道题,我算是佩服的五体投地,头懵眼花加恶心,在网上苦苦寻觅,可是就找不到真的对的答案,网上的方法千篇一律,可是他们都是错的,感觉是有一个人没有读懂题,结果千千万万个博主都没有读懂它,对此我感到很无奈,网上的东西总是有人不假思索的模仿借鉴,给我的学习造成了很多不必要的麻烦,所以我自己也是根据自己查到的一些思路加以整理,敲了一个四五十行的代码,虽然不简洁,不是最好的办法,但是感觉对的上题了,所以拿出来保存在博客中,以示提醒:1,不懂了,不明白了,多上网上查查别人的思路和代码,要从别人的代码中学习到一些有用的东西;2,对别人的代码,要多斟酌,多推敲,取其精华,弃其糟粕;3,绝对不能盲目抄袭别人的代码,一定要融入自己的见解……以上是我这两天遨游在网上的代码的海洋中的的感触!
网上各路大神的牛逼代码铺天盖地而来,感觉自己好渺小,但是我要努力去从他们的代码中学到有用的,也让自己一点点牛逼起来!
废话少说,看题:

Description
题目描述:
大家知道,给出正整数n,则1到n这n个数可以构成n!种排列,把这些排列按照从小到大的顺序(字典顺序)列出,如n=3时,列出1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1六个排列。

任务描述:
给出某个排列,求出这个排列的下k个排列,如果遇到最后一个排列,则下1排列为第1个排列,即排列1 2 3…n。
比如:n = 3,k=2 给出排列2 3 1,则它的下1个排列为3 1 2,下2个排列为3 2 1,因此答案为3 2 1。
Input
第一行是一个正整数m,表示测试数据的个数,下面是m组测试数据,每组测试数据第一行是2个正整数n( 1 <= n < 1024 )和k(1<=k<=64),第二行有n个正整数,是1,2 … n的一个排列。
Output
对于每组输入数据,输出一行,n个数,中间用空格隔开,表示输入排列的下k个排列。
Sample Input
3
3 1
2 3 1
3 1
3 2 1
10 2
1 2 3 4 5 6 7 8 9 10
Sample Output
3 1 2
1 2 3
1 2 3 4 5 6 7 9 8 10
题就是这样,晦涩难懂,好多人都无法正确理解题意,我感觉,读不懂题有两方面原因:1,智商问题,语文没学好,关键是读书少;2,缺乏耐心和细心,没有认真审题,支差应付,盲目抄袭他人。
过多的讲这道题我已经有心无力,我把注释都打的清清楚楚,直接上代码就好了。

#include<stdio.h>//头文件
int a[10][1024];//定义数列,存储初始数据,并推出下K个排列,更新数据存储
void A(int i,int p,int n)//定义一个简单的调换数据并排序的函数
{
    int j,k,l;//定义变量
    k=i;//储存初始i的值
    for(i=i+1;i<n;i++)//寻找刚好比a[p][k]大一的数并调换位置
    {
        if (a[p][i]==a[p][k]+1)//符合条件,则
        {
            j=a[p][k];//调换位置
            a[p][k]=a[p][i];//调换位置
            a[p][i]=j;//调换位置
            break;//跳出此循环
        }
    }
    for (i=k+1;i<n;i++)//将后面的数据进行排序,选择排序
    {
        for(l=i+1;l<n;l++)//将后面的数据进行排序,选择排序
        {
            if (a[p][i]>a[p][l])//当前面的数据大于后边的则调换位置
            {
                j=a[p][l];//调换位置
                a[p][l]=a[p][i];//调换位置
                a[p][i]=j;//调换位置
            }
        }
    }
}
void B(int k,int n,int p)//定义一个递归函数,用来实现数据的循环,当数据是最后一个排列时,可以借此实现下一个排列从1,2,3……n开始
{
    int i,q,o,l;
    while (k--)//循环一次得到下一个排列,下k个排列循环k次
    {
        for (i=n-2;i>=0;i--)//从倒数第二个数开始检索
        {
            if (a[p][i]<a[p][i+1])//当相邻的两个数前者大于小于后者,则
            {
                A(i,p,n);//调用函数,即上方自定义函数,简单的调换数据并排序
                break;//跳出此循环
            }
        }
        if(i<0)//当排列中没有前者小于后者的两个相邻数据时
        {
            l=n-1;//用来表示数组中的最后一个数据的
            for(q=0;q<n/2;q++)//当出现这种情况时,肯定是由n到1依次排列,按题意需要调换对称位置的数据
            {
                o=a[p][q];//调换数据
                a[p][q]=a[p][l-q];//调换数据
                a[p][l-q]=o;//调换数据
            }
            B(k,n,p);//递归
        }
    }
}
int main()//主函数
{
    int b[10];//定义一个数组存储所有的n
    int i,n,m,k,p;//定义数据,变量
    scanf("%d",&m);//输入m,表示下面有m组数据
    for(p=0;p<m;p++)//输入m组数据并处理数据
    {
        scanf("%d%d",&n,&k);//输入每组数据的n与k
        for (i=0;i<n;i++)//循环n次存储1到n(无顺序)
        {
            scanf("%d",&a[p][i]);//输入1到n中的数据(每个数据存储一次),保存至数组a[p]中
        }
        b[p]=n;//存储每组数据的n到数组p中
        B(k,n,p);
    }
    for(p=0;p<m;p++)//循环m次,输出m组数据
    {
        for(i=0;i<b[p];i++)//每组数据输出前b[p]项
        {
            printf("%d ",a[p][i]);//输出每组数据,每组数据占一排
        }
        printf("\n");//换行
    }
    return 0;//返回0
}    
代码就是这么扯淡,函数套函数,还用到了递归,大致的思路我也啰嗦两句,免得日后看不懂。
问题关键在于:给出一个排列a1、a2、a3…an后,如何求出下一个刚好比它大的排列?举个例子,1 5 3 2 4的下一个排列就是1 5 3 4 2,1 5 4 3 2的下一个排列就是 2 1 3 4 5。方法:从排列的倒数第二个数向前查找满足ai<a(i+1)的第一个数ai,即查找第一个左边小于右边的相邻的两个数。从a(i+1)…an中查找最小的比ai大的数aj,即刚好比ai大的数。将ai与aj进行交换,然后对a(i+1)…an按从小到大进行排序。得到的就是所要求的下一个排列。求下k个排列时,重复执行上面过程k次即可。

如求1 5 4 3 2的下一个排列,查找ai

你可能感兴趣的:(poj,排列,审题)