外星人计数

// 外星人计数法.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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值