康托展开和逆康托

彻底搞懂康托和逆康托
康拓展开
康托展开表示的是当前排列在n个不同元素的全排列中的名次
ans = an*(n-1)! + an-1 * (n-2)! + …+ a2 * 1! + a1 * 0!
其中表示第i个元素在未出现的元素中排列第几。
举个简单的例子:
对于排列4213来说,
(1)4在4213中排第3,注意从0开始,
(2)2在213中排第1,
(3)1在13中排第0,
(4)3在3中排第0,即:
这样得到4213在所有排列中排第ans=20(除第一个以外,比如1234这个排列,第20个是4132,第21个才是4213)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 18;
ll f[maxn];
void init()
{
    ll sum = 1;
    for (ll i = 1; i <= 18; i++) {
        sum *= i;
        f[i] = sum;
    }
}

ll Work(string str)
{
    int len = str.length();
    ll ans = 0;
    for (int i = 0; i < len; i++)
    {
        int tmp = 0;
        for (int j = i+1; j < len; j++)
        {
            if (str[j] < str[i]) tmp++;
        }
        ans +=  tmp*f[len-i-1];
    }
    return ans;
}
int main()
{
    init();
    string s = "bckfqlajhemgiodnp";
    cout << Work(s) << endl;
    return 0;
}

//22952601027516

Java代码实现:

package 康拓;

//求当前排列是第几个排列
import java.util.*;
public class 康拓 {
    static Long[] f = new Long[100];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        init();
        while (in.hasNext()) {
            String a = in.next();
            int n = a.length();
            solve(a, n);
        }
    }
    public static void init() {
        f[0] = 1L;
        f[1] = 1L;
        for (int i = 2; i < 20; i++) {
            f[i] = (Long)f[i-1]*i;
        }
    }
    private static void solve(String str, int n) {
        int ans = 0;
        for (int i = 0; i < str.length() - 1; i++) {
            char a = str.charAt(i);
            int cnt = 0;
            for (int j = i+1; j < str.length(); j++) {
                char b = str.charAt(j);
                if (b < a) {
                    cnt++;
                }
            }

            ans = (int) (ans + (cnt*f[str.length() - i - 1]));
        }
        System.out.println(ans);
    }
}

还有就是逆康托:
 例 {1,2,3,4,5}的全排列,并且已经从小到大排序完毕
找出第96个数
  (1)首先用96-1得到95
  (2)用95去除4! 得到3余23
  (3)用23去除3! 得到3余5
  (4)用5去除2!得到2余1
  (5)用1去除1!得到1余0
计算:
(1)有3个数比它小的数是4,所以第一位是4
(2)有3个数比它小的数是4但4已经在之前出现过了,所以是5(因为4在之前出现过了所以实际比5小的数是3个)
(3)有2个数比它小的数,是3
(4)有1个数比它小的数,是2
(5)最后一个数只能是1
所以这个数是45321

那如果一个原始数是字符串,或者大整数呢,
下面考虑了long long 范围内也就是字符串18内及以内的数的排列查找第几个数。
比如传入m = 1234, n = 16,那这个结果就是3241,
比如传入字符串m =“abcdefghijklmnopq”, n = 22952601027517,
那这个结果就是”bckfqlajhemgiodnp”,也就是它的第22952601027517排列数是”bckfqlajhemgiodnp”。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//     12345
ll f[6];
void init(string m)
{
    ll sum = 1;
    for (ll i = 1; i <= m.length(); i++)
    {
        sum *= i;
        f[i] = sum;
    }
}
void Work(string m, ll n)
{
    n--;
    vector<char> v;
    vector<char> result;
    for (ll i = 0; i < m.length(); i++)
        v.push_back(m[i]);
    //注意下标vector的下标是从0开始的
    for (ll i = m.length()-1; i >= 1; i--)
    {
        ll r = n % f[i];
        ll s = n / f[i];
        n = r;
        //sort(v.begin(), v.end());
        result.push_back(v[s]);
        v.erase(v.begin()+s);
    }
    //最后一个
    result.push_back(v[0]);
    for (ll i = 0; i < m.length(); i++)
    {
        cout << result[i];
    }
    cout << endl;

}

int main()
{
    string m;
    ll n;//第 n 个
    cin >> m;
    cin >> n;
    init(m);
    Work(m, n);
    return 0;
}

Java代码实现:

package 康拓;

//给定一定数n,求指定排列的第n个数是啥
import java.util.ArrayList;
import java.util.Scanner;

public class 逆康拓 {
    static Long[] f = new Long[100];
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        init();
        while (in.hasNext()) {
            String a = "abc";
            ArrayList<Character> list = new ArrayList<Character>();
            for (int i = 0; i < a.length(); i++) {
                list.add(a.charAt(i));
            }

            int n = in.nextInt();
            solve(n, list, a.length());
        }
    }
    public static void init() {
        f[0] = 1L;
        f[1] = 1L;
        for (int i = 2; i < 20; i++) {
            f[i] = (Long)f[i-1]*i;
        }
    }
    private static void solve(int n, ArrayList<Character> list, int t) {
        String result = "";
        n -= 1;
        for (int i = t-1; i > 0; i--) {
            int k = (int)(n/(f[i]));
            result = (result + (list.get(k)));
            list.remove(k);
            n = (int)(n%(f[i]));
        }
        if (list.size() != 0) result = result + list.get(0);
        System.out.println(result);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_34649947/article/details/66978704