// 外星人计数法.cpp : 定义控制台应用程序的入口点。
//DJ.W 2013.3.30
/*问题描述:
下面是外星人计数和人类计数的对应
外星人计数 人类计数
1 1
12 2
21 3
123 4
132 5
213 6
231 7
312 8
321 9
1234 10
.... ...
要求输入人类计数,计算并输出对应外星人计数表示
例:输入:234 输出:153426
*/
/*
思路:按照上面说给出的规律:
外星人计数按照数字个数由少到多,排列值由小到大(按照人类计数)的顺序依次对应于人类计数
那么对于一个人类计数 n 可化为 n = 1!+2!+3!+...i!+k (0 < k<=(i+1)! )
这样我们可以知道n对应的外星人计数是由i+1位数(1..i+1)组成的,它对应的具体排列就是
i+1位数所有排列中,由小到大的第k个数。
如 : 10 = 1!+2!+3!+1 那么10的外星人计数就是(1 2 3 4)四位数所有排列中,第一小即最小的排列1234
又如: 7 = 1!+2!+4 那么7的对应外星人计数就是(1 2 3)三位数由小到大排列的第四个数 即231
这是解决问题的第一步
第二步:
此时此题就化为了如果在i个数的排列中,得到由小到大的第k个数(0<k<=(i!))。
如i=4时的排列: 1234 1243 1324 1342 1423 1432 2134......
我们可以观察得出,首位的1在经过了(i-1)!=3!=6次之后,才变为2,而第二位的2经过了(i-2)!=2次之后才变为3
依次类推:自左向右第j位在经过(i-j)!之后才变为下一个数,并且只要它前面的j-1个数没变,那么它的下一个数必定
是比它大的可用数 如在 2134 2143 2314 2341 2413 2431 中,当左边第一位2没变时,第二位是递增的,当第一第二位
没变时,第三位是递增的。。。
解释这些可能让问题更糊涂,其实上面主要就是想证明它的递归特性。
下面举一个数字的完整转换,比如对于输入234
234 = 1!+2!+3!+4!+5!+81
即234的表示是(1 2 3 4 5 6)六个数由小到大的第81个排列
由于81<5!=120 因此后五位变化还没有到一个周期,所以首位为1 此时还剩后五位2 3 4 5 6
81 - 4! * 3= 9 后4位变化了三个周期,因此它们的前一位(第二位)递增了3次,即在2 3 4 5 6中的5 此时还剩后四位2 3 4 6
9 - 3! = 3 后三位变化了一个周期,因此它们的前一位(第三位)递增了1次,即为2 3 4 6中的3 还剩后三位2 4 6
3 - 2! = 1 后两位递增了一个周期,那么第四位递增一次,为2 4 6中的4 还剩2 6
此时只剩下1,即代表是2 6 两个数表示中最小的, 即26
注意:如果是235 即最后是4-2! 不能算成4-2!*2=0 0意味着第0小的排列 无意义,而4-2!=2意味着最后两个数最大的排列
综上 234 的外星表示为 153426
*/
#include "stdafx.h"
#include <iostream>
#include<windows.h>
using namespace std;
#include<vector>
int _tmain(int argc, _TCHAR* argv[])
{
//获取输入
cout<<"输入人类数字: "<<endl;
int n;
cin>>n;
while(n<0)
{
cout<<"n < 0无法转换,请重新输入: "<<endl;
cin>>n;
}
//第二步,得到k和i 使得n = 1! + 2! + 3! + ... + i! + k
int acc = 0; //1! + 2! + ... + i!
int i = 0;
int ifact = 1; //i!
int iplusfact; //(i+1)!
while(true)
{
iplusfact = (i+1)*ifact;
if (n - acc <= iplusfact)
break;
acc += iplusfact;
i++;
ifact = iplusfact;
}
int k = n-acc;
//第二步 得到i!个排列中由小到大第k个排列
int nCircle; //当前递增了多少个周期
vector<int> vNumCanUse; //当前可用的数
for (int j=1; j<=i+1; j++)
vNumCanUse.push_back(j);
cout<<"转换为外星表示法为:"<<endl;
while (i > 1)
{//当前计算后面i+1位中的最高位上的数字
nCircle = (k-1)/ifact; //此处用k-1是为了保证 4 = 2! + 2 而不是 4 = 2!*2
cout<<vNumCanUse.at(nCircle); //取得当前可用的第nCircle小的数 作为当前位的数
vNumCanUse.erase(vNumCanUse.begin()+nCircle); //移除该数
//转到后面i位
k = (k-1)%ifact+1;
ifact = ifact/i;
i--;
}
//对于最后两位数 即i==1时 可直接判断
if (k == 1)
{
cout<<vNumCanUse.at(0);
cout<<vNumCanUse.at(1);
}
else if(k == 2)
{
cout<<vNumCanUse.at(1);
cout<<vNumCanUse.at(0);
}
else
cerr<<"Error In Convert !"<<endl;
cout<<endl;
return 0;
}
外星人计数
最新推荐文章于 2021-11-17 09:16:50 发布