现在的位置: 首页 > 综合 > 正文

全排列相关

2013年07月10日 ⁄ 综合 ⁄ 共 3073字 ⁄ 字号 评论关闭

 

1 求第k个排列
2 求给定排列在全排列中的序号
3 求下k个排列

#include <iostream>
#include <stdlib.h>
//#include <algorithm>
#include <map>
#include <time.h>

using namespace std;

unsigned int mp[1000+1] = {1, 1, 2, 6, 24, 120, 720};
unsigned int Fatorail(int n)
{
    unsigned int value=1;
    if (mp[n])
        return mp[n];
    for (int i=1; i<=n; i++)
        value *= i;
    mp[n] = value;
    return value;
}

void reverse(int *p, int n)
{
    int i=0, j=n-1;
    while (i<j)
    {
        swap(p[i], p[j]);
        i++, j--;
    }
}
/**
    @brief 给定一个排列p[s...e], 找出这样一个位置pos,
           使得从pos开始的后面m为还剩下的排列数大于k
*/
int LeftPremutation(int *p, int s, int e, int kk, int &total)
{
    int *t = new int[e-s+1];
    int i,j;

    j=0;
    unsigned int sum=0;
    t[0] = p[e];
    for (i=e-1; i>=s; i--)
    {
        int k;
        for (k=j; k>=0; k--)
        {
            //插入,保持有序
            if (t[k]>p[i])
                t[k+1] = t[k];
            else
                break;
        }
        t[k+1] = p[i];    
        
        j++;
        //p[i]的前面有k个数字 k的后面有j-k-1个数字
        
        if (j-k-1>0)
            sum += (j-k-1) * Fatorail(e-i);
        
        if (sum >= kk)
        {
            total = Fatorail(e-i+1);
            return i;
        }
    }
    total = sum;
    return -1;
}
/**
    @brief 求p[s...e]这个几个元素组成的第k个排列,结果存储在p[s...e]中
*/
void PermutationByOrder(int *p, int s, int e, int k)
{
    map<int, bool> flg;
    int i, j;

    //初始化所有元素都未被选中
    for (i=s; i<=e; i++)
        flg[p[i]]=true;

    j=s;
    while (j<=e)
    {
        i=1;
        int t=Fatorail(e-j);   //后面有e-j个数位
        while ( (t*i)<k ) ++i; 

        k -= (i-1)*t;
        int cnt=i;
        for (map<int, bool>::iterator iter=flg.begin(); iter!=flg.end() && cnt; iter++)
        {
            if (iter->second == true)
                cnt--;
            if (cnt==0)
            {
                flg[iter->first] = false;
                p[j++] = iter->first;
                break;
            }
        }
    }

}
/**
    @brief 生成1, 2, ... n,  n个数构成的全排列中的k个排列
 */
void PermutationByOrder(int *p, int n, int k)
{
    //int values[] = {1, 1, 2, 6, 24, 120}; //n!的值
    bool *flg = new bool[n+1];
    //int *t = new int[n];
    int i, j;

    for (i=0; i<=n; i++)
        flg[i] = true;
    j=0;
    while (j<n)
    {
        i=1;
        int t=Fatorail(n-j-1);
        while ((t*i)<k) i++;
        
        k -= (i-1)*t;
        int cnt=i;
        for (i=1; i<=n && cnt; i++)
        {
            if (flg[i])
                cnt--;
            if (cnt==0)
                break;
        }
        p[j++] = i;
        flg[i] = false;
    }
    delete [] flg;
}
/**
    @brief 给定一个排列,返回它在全排列中的顺序
    对于四位数的:如果第一位是4,即4xxx
    那么说明第一位是1,2,3的排列已经列举过了。而1xxx总共有3!种。
    2xxx, 3xxx也有3!种。规律就是: 4是第4个数,它的前面有3个数,那么先不
    考虑后面,前面的排列至少为3*3!(3!:4的后面有3位数)。
    接下来还要考虑后面的,类似的分析方法。
    43xx:3*3!+2*2!
    432x: 3*3!+2*2!+1
    4321: 3*3!+2*2!+1+1 = 24 = 4!
*/

int OrderByPermutation(int *p, int n)
{
    bool *flg = new bool[n+1];
    int i, j;

    for (i=1; i<=n; i++)
        flg[i] = true;

    unsigned int sum=0;
    for ( i=0; i<n; i++)
    {
        int cnt=0;
        for (j=1; j<p[i]; j++)
        {
            if (flg[j])
                cnt++;
        }
        flg[p[i]] = false;
        if (cnt)
            sum += Fatorail(n-i-1)*cnt;
    }
    return sum+1;
}
/*
    @brief 给定一个排列p[s...e],返回p[s...e]在由这e-s+1个元素构成的全排列中的序号
*/
int OrderByPermutation(int *p, int s, int e)
{
    map<int, bool> flg;
    int i, j;

    //初始化所有元素都未被选中
    for (i=s; i<=e; i++)
        flg[p[i]]=true;

    j=s;
    unsigned int sum=0;
    while (j<e)
    {
        int cnt=0;        
        //p[j]前面有cnt个数
        for (map<int, bool>::iterator iter=flg.begin(); iter!=flg.end(); iter++)
        {
            if (iter->first==p[j])
                break;
            if (flg[iter->first]==true)
                cnt++;
        }
        flg[p[j]] = false;
        if (cnt)
            sum += Fatorail(e-j)*cnt;
        j++;
    }
    return sum+1;
}

void Print(int *p, int n)
{
    for (int i=0; i<n; i++)
    {
        if (i != n-1 )
            cout << p[i] << " ";
        else
            cout << p[i] << endl;
    }
}

int cmp(const void *a, const void *b)
{
    return *(int *)a - *(int *)b;
}
int main()
{
    int m;
    cin >> m;
    
    int i=0;
    while (i++<m)
    {
        int n, k;
        cin >> n >> k;
        int *p = new int[n];

        int j=0;
        while (j<n)
        {
            cin >> p[j];
            j++;
        }
        if (n<5 && k>=mp[n])
            k %= mp[n];
        int total;
        int oldk = k;
    
        while (1)
        {
            int k1=LeftPremutation(p, 0, n-1, k, total);
            if (k1==-1)
            {
                //升序排列
                qsort(p, n, sizeof(int), cmp);
                //reverse(p, n);
                if (total)
                    oldk -= (total+1);
                else
                    oldk--;
                //oldk--;
                k=oldk;
                if (oldk == 0)
                {
                    Print(p, n);
                    break;
                }
                continue;
            }
            int k2=OrderByPermutation(p, k1, n-1);
            if (k2+oldk>total)
            {
                k = total+1;
                continue;
            }
            PermutationByOrder(p, k1, n-1, k2+oldk);
            Print(p, n);
            break;
        }
    
        delete [] p;
    }    
    return 0;
}

 

 

抱歉!评论已关闭.