• 康托展开和康托展开的逆运算


    八数码问题不用康托展开判断重复8s,用康托展开判断重复30MS。康托展开最大最明显的作用就是在判断状态是否重复方面了,其实属于hash的一个技巧。

    一、康托展开
    【问题背景】对于一个有n个不同元素的集合{1,2,3,4,...,n}的从小到大排序(从大到小 同理)的全排列 显然它有n!项。如n=4,那么就有4!=4×3×2×1=24项。
    与自然数1,2,3,4,-----n!与之一一对应。比如 1~4四个数的全排列按字典序如下:
    1234:第1个
    1243:第2个
    1324:第3个
    1342:第4个
    1423:第5个
    1432:第6个
    2134:第7个
    2143:第8个
    2314: 第9个
    2341:第10个
    2413:第11个
    2431:第12个
    3124:第13个
    3142:第14个
    3214:第15个
    3241:第16个
    3412:第17个
    3421:第18个
    4123:第19个
    4132:第20个
    4213:第21个
    4231:第22个
    4312:第23个
    4321:第24个

    【主要问题】
    例1:求4132是第几个排列? 看上面就知道答案就是:20。 那么是怎么算的呢?
    解:总共4个数,所以n=4.ans:=0;
    第一个数是4,研究比4小的并且还没有出现过的数有3个:1,2,3。那么ans:=ans+ 3*(n-1)!
    所以 ans:= ans+ 3* 3*2*1 =18
    第二个数是1,研究比1小的并且还没有出现过的数为 0个。那么ans:=ans+ 0 * (n-2)!,那么ans不变。
    第三个数是3,研究比3小的并且还没有出现过的数为1个:2。那么ans:=ans+ 1* (n-3)!,那么ans:=18+1* 1=19
    第四个数是2,研究比2小的并且还没有出现过的数为0个。那么ans不变。其实最后一个可以不研究了,比它大和比它小的全都出现过了。 最后ans怎么等于19啊??代表它前面有19个排列嘛,那么4132自己就是第20个罗( 最后ans:=ans+1)
    例2:问45231是第几个排列?
    4 5 2 3 1
    ans:= 3*4! + 3*3! + 1*2! + 1*1! + 0*0! + 1 =94

    #include<cstdio>
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll n;
    ll a[17],d[17];
     
    ll kangtuo()
    {
        bool bo[17];memset(bo,false,sizeof(bo));
        ll sum=0;
        for(ll i=1;i<=n-1;i++)
        {
            ll k=0;
            for(ll j=i+1;j<=n;j++)   //看第i个数字,它后面有多少数字比它小
         		if(a[i]>a[j])k++;
            sum=sum+k*d[n-i];
          //  cout<<i<<"   "<<k<<endl;
         //   bo[a[i]]=true;
        }
        sum++;
        return sum;
    }
    int main(){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        d[0]=1;for(int i=1;i<=15;i++)d[i]=d[i-1]*i;
        printf("%lld
    ",kangtuo());
        return 0;
    }
    

      当数据范围较大时,可以用树状数组进行优化。。洛谷P5367

    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+10;
    const ll p=998244353;
    ll n,a[maxn],ans;
    ll f[maxn],c[maxn];
    void update(int x,int k)
    {
    	for(int i=x;i<=n;i+=i&-i)
    		c[i]+=k;
    }
    int deal(int x)
    {
    	int t=0;
    	for(int i=x;i>0;i-=i&-i)
    		t+=c[i];
    	return t;
    }
    int main()
    {
    	cin>>n;
    	f[0]=1;
    	for(int i=1;i<=n;i++)
    	f[i]=f[i-1]*i%p;
    	for(int i=1;i<=n;i++) update(i,1);
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		ans=(ans+(deal(a[i])-1)*f[n-i]%p+p)%p;
    		//查找比a[i]小的数字有多少个(还没有出现过的数字中) 
    		update(a[i],-1);
    	}
    	cout<<ans+1;
    	return 0;
    }
    

      

    二、康托展开的逆运算
    我把之前的div改为/,div是pascal语言的运算符号,/是C++的,意思都一样就是求两个整数除法运算后的商(不理会余数)
    例3:1~5从小到大全排列中,找出第96个排列?
    解:首先设x1x2x3x4x5, (x1等于?不知道),用96-1得到95,表示x1x2x3x4x5前面有95个序列。
    第一个数x1,假设x1目前有k个比x1小的并且还没有出现过的数,那么
    k:= 95 / (n-1)! = 95 / 24=3, 也就是有3个比x1小并且没有出现过的数,那么x1=4.
    95变成95-3×24=23
    第二个数x2,假设x2目前有k个比x2小的并且还没有出现过的数,那么
    k:= 23 / (n-2)! = 23 / 6 = 3, 也就是有3个比x2小并且没有出现过的数,那么x2=5.(有3个数比它小的数是4,但4已经在之前出现过了,所以是5)
    23变成 23 – 3 * 6 = 5
    第三个数用5去除2! 得到2余1,那第三个数就是还没有出现过的第3小的数3(5变成1)
    第四个数用1去除1! 得到1余0,那第四个数就是还没有出现过的第2小的数2
    第五个数就是最后还没有出现的那个。
    所以这个数是45321

    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll n;
    ll a[17],d[17];
    void _kangtuo(ll sum)
    {
    	sum--;
    	bool bo[17];memset(bo,true,sizeof(bo));
    	for(ll i=1;i<=n;i++)
    	{
    		ll k=sum/d[n-i];
    		sum-=k*d[n-i];
    		ll j;
    		for(j=1;j<=n;j++)
    			if(bo[j]==true)
    			{
    				if(k==0)break;
    				k--;
    			}
    		bo[j]=false;
    		a[i]=j;
    	}
    	for(ll i=1;i<n;i++)printf("%lld ",a[i]);
    	printf("%lld
    ",a[n]);
    }
    int main(){
    	scanf("%lld",&n);
    	d[0]=1;for(int i=1;i<=15;i++)d[i]=d[i-1]*i;
    	ll k;scanf("%lld",&k);
    	_kangtuo(k);
    	return 0;
    }
    

      

  • 相关阅读:
    Struts2基于XML配置方式实现对action的所有方法进行输入校验
    Lucene对index操作
    Lucene自定义同义词分词器
    Lucene自定义排序
    使用CGlib实现AOP功能
    实战才是王道:工厂模式、三层架构、反射、多数据库问题
    一图看懂mybatis执行过程
    java synchronized 关键字原理
    Centos7 Redis3.0 集群搭建备忘
    关于泛型接口的探讨
  • 原文地址:https://www.cnblogs.com/cutemush/p/13549835.html
Copyright © 2020-2023  润新知