进制转换小结(大数的任意进制转换)spoj429 Simple Numbers Conversion,poj 1220NUMBER BASE CONVERSION

4 篇文章 0 订阅
2 篇文章 0 订阅
进制转换小结
进制转换的两种思路,一种是得到的结果字符串就是答案,另一种是得到反序的答案,反转一下就行了.
/*************************************************************************
	> File Name: basic_10.cpp
	> Author: gwq
	> Mail: 457781132@qq.com 
	> Created Time: 2014年11月06日 星期四 09时05分38秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;

#define N 20

const int base = 16;
char mp[] = "0123456789ABCDEF";
char ans[N];

//直接求,不用反序
//写法复杂
void getans(int n)
{
	//tmp可能会爆int
	ll tmp = 1;
	while (tmp <= n) {
		tmp *= base;
	}
	int cnt = 0;
	tmp /= base;
	//注意n为0的情况
	if (n == 0) {
		ans[cnt++] = '0';
	}
	//从最高位依次找出这一位是什么
	while (tmp > 0) {
		ans[cnt++] = mp[n / tmp];
		n %= tmp;
		tmp /= base;
	}
	ans[cnt] = '\0';
}

//传统方法
//写法简单
void getans2(int n)
{
	int cnt = 0;
	//注意n为0的情况
	if (n == 0) {
		ans[cnt++] = '0';
	}
	while (n != 0) {
		ans[cnt++] = mp[n % base];
		n /= base;
	}
	ans[cnt] = '\0';
	//反序
	reverse(ans, ans + cnt);
}

int main(int argc, char *argv[])
{
	int n;
	while (scanf("%d", &n) != EOF) {
		//printf("%X\n", n);
		//getans2(n);
		getans(n);
		printf("%s\n", ans);
	}

	return 0;
}

/*
基础练习 十进制转十六进制  
时间限制:1.0s   内存限制:512.0MB
      
问题描述
  十六进制数是在程序设计时经常要使用到的一种整数的表示方式。
它有0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F共16个符号,分别表示十进制数的0至15。
十六进制的计数方法是满16进1,所以十进制数16在十六进制中是10,
而十进制的17在十六进制中是11,以此类推,十进制的30在十六进制中是1E。
  给出一个非负整数,将它表示成十六进制的形式。
输入格式
  输入包含一个非负整数a,表示要转换的数。0<=a<=2147483647
输出格式
  输出这个整数的16进制表示
样例输入
30
样例输出
1E
*/

这个是大数十六进制转成八进制,也有两种思路,一种是先把十六进制转成二进制,因为每个十六进制数转成四个二进制数,然后再把二进制数转成八进制,每三个二进制数转成一个8进制数。第二种思路是,每三个十六进制数可以转成四个八进制数,处理好这个十六进制数长度不是三的情况就好了,输出的时候也要注意不要有前导零。
我在写的时候使用了sscanf,莫名奇妙的超时了,可能是因为sscanf的效率不是很高,但奇怪的是我下载蓝桥杯的数据在本地跑是秒出,这个问题还有待考证。
/*************************************************************************
	> File Name: basic_12.cpp
	> Author: gwq
	> Mail: 457781132@qq.com 
	> Created Time: 2014年11月06日 星期四 10时13分15秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;

#define N 100010

char mp[] = "0123456789ABCDEF";
char str[N];

/*
 * 每三位16进制数可以变成四位八进制数,这里利用这个性质
 * 注意处理好字符串长度不为三的情况。
 * 另一种思路是在前边补上0,使得字符串的长度是三的倍数
 * 然后,处理的时候不输出前导0。
 */
int main(int argc, char *argv[])
{
	int t;
	scanf("%d", &t);
	while (t--) {
		scanf("%s", str);
		int len = strlen(str);
		int num = 0;
		for (int i = 0; i < len % 3; ++i) {
			num = num * 16 + (strchr(mp, str[i]) - mp);
		}
		//不能有前导0
		//%04o输出为4位8进制数,不足四位就补0
		if (len % 3 != 0) {
			printf("%o", num);
		}
		for (int i = len % 3; i < len; i += 3) {
			int tmp = 0;
			//使用sscanf函数读3个16进制,不知道怎么超时了
			//下载蓝桥杯的数据在本地跑是秒出呀!
			//sscanf(str + i, "%3x", &tmp);
			//开始把j=i写成j=0了,坑!
			for (int j = i; j < i + 3; ++j) {
				tmp = tmp * 16 + (strchr(mp, str[j]) - mp);
			}
			//不能有前导0
			if (i == 0) {
				char s[10];
				sprintf(s, "%o", tmp);
				if(strlen(s) < 4) {
					printf("%o", tmp);
				} else {
					printf("%04o", tmp);
				}
			} else {
				printf("%04o", tmp);
			}
		}
		printf("\n");
	}

	return 0;
}

/*
基础练习 十六进制转八进制  
时间限制:1.0s   内存限制:512.0MB
      
问题描述
  给定n个十六进制正整数,输出它们对应的八进制数。
输入格式
  输入的第一行为一个正整数n (1<=n<=10)。
  接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六
进制正整数,每个十六进制数长度不超过100000。
输出格式
  输出n行,每行为输入对应的八进制正整数。
注意
  输入的十六进制数不会有前导0,比如012A。
  输出的八进制数也不能有前导0。
样例输入
2
39
123ABC
样例输出
71
4435274
提示
  先将十六进制数转换成某进制数,再由某进制数转换成八进制。
*/

上边的 都是有点特殊的进制转换,数的范围比较小。
上边的第二个题目是特殊的大数进制转换,即将r进制转换为s进制,其中r^m=s,那么,就可以将m位的r进制数转成一位s进制数,复杂度是O(r进制数的位数),反过来,将s进制转成r进制也是这样,每位s进制的数转成m位r进制的数,复杂度同上。
现在问题来了,如果不是特殊的,而是一般的大数进制转换呢?我们假定进制数最大为62,即符号0-9,A-Z,a-z,分别表示0,1,...9,10,11,...,35,36,...60,61。
先假定将r进制数转换成s进制的数,其中r>s。先来看看小范围的进制转换是怎么样的,举一个十进制数转成二进制数的例子,如将十进制的12转成二进制,就是用12来除2得到商6,余数0,余数0是二进制的最低位,然后再用6除2得到商3,余数0,余数0是二进制的次低位,依次类推就得到12的二进制数1100,因为这里是大数,我们自然就想到用大数类来进行这个运算,当然这也是可以的,不过效率就不怎么高了。
现在我们来模拟一下除法的运算,我们按每一位来除以2,开始12是十位上1,个位上为0,先用十位上的1除以2得到商0,余数为1,然后,计算个位上的2,因为前边留下了余数1,所以计算的时候要加上这个余数,当然还要乘上它的权值,这里是十进制,就是乘以10,所以,个位上就是1*10 + 2得12除以2得到商6,余数0。那么我们最终得到的商是06,余数就是最后一位上得到的余数0,去掉商的前导0后,就可以按照类似的方法迭代,直到得到的商是0,那么我们就可以用一个数组来存这个大的任意进制数,经过迭代得到每一轮的余数,然后再将余数翻转一下,就得到了相应进制的数。
下图是12转成二进制的示意图:

上边说的是r大于s的情况,其实这个方法也适用于r小于或等于s的情况,不过当r等于s时,其实已经没必要转换了,在效率要求不高的情况下,可以忽略这种差异。
这种写法在实现的时候要去掉前导0,可能就要涉及到数组元素的移动,这就降低了效率,可以用一个变量更新商的开头位置,就避免了数组的移动,先前没改之前在spoj上429题 Simple Numbers Conversion第五类数据是超时,改成这种写法后,第五类数据跑了19+秒(时限是20秒),真是凶险,后来我再怎么改也降低不了时间了,看来还是水平问题。关于spoj429题,可以参考http://blog.csdn.net/rappy/article/details/1737671这篇文章,他跑了12+秒,我按照他的思路写过后还是跑的19+秒,不知道是什么原因。
下面是poj上的1220题,它的数据范围不是很大,用的数组移动也过了(还是0ms),代码如下:
/*************************************************************************
	> File Name: 1220.cpp
	> Author: gwq
	> Mail: gwq5210@qq.com 
	> Created Time: 2014年11月07日 星期五 23时54分08秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;

const double esp = 1e-5;

#define N 10010

int r, s;		//将r进制的数变成s进制
int lnum, ltmp, lans;	//对应数组的长度
char str[N];		//以r进制表示的数的字符串
int num[N];		//以r进制表示的数的整数
int tmp[N];		//保存str除以s的商,迭代,直到str为0
char ans[N];		//保存以s进制的最后结果
char mp[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

//整数变成对应的字符
char itoc(int i)
{
	return mp[i];
}

//字符变成对应的整数
int ctoi(char c)
{
	return strchr(mp, c) - mp;
}

int main(int argc, char *argv[])
{
	int t;
	scanf("%d", &t);
	while (t--) {
		scanf("%d%d%s", &r, &s, str);
		lnum = strlen(str);
		//将字符变成对应的整数
		for (int i = 0; i < lnum; ++i) {
			num[i] = ctoi(str[i]);
		}
		lans = 0;
		clr(ans, 0);
		//迭代到num的长度为0,也就是它表示的数为0
		while (lnum >= 1) {
			int rem = 0;
			clr(tmp, 0);
			for (int i = 0; i < lnum; ++i) {
				tmp[i] = (num[i] + rem * r) / s;
				rem = (num[i] + rem * r) % s;
			}
			ans[lans++] = itoc(rem);
			//去掉tmp的前导0
			int pos = 0;
			while (pos < lnum && tmp[pos] == 0) {
				++pos;
			}
			int cnt = 0;
			//这里不能用strcpy,因为已经不是字符串了
			for (int i = pos; i < lnum; ++i) {
				num[cnt++] = tmp[i];
			}
			lnum = cnt;
		}
		//得到的是逆序的,要翻转一下
		reverse(ans, ans + strlen(ans));
		printf("%d %s\n", r, str);
		printf("%d %s\n\n", s, ans);
	}

	return 0;
}

/*
NUMBER BASE CONVERSION
Time Limit: 1000MS		Memory Limit: 10000K
Total Submissions: 4349		Accepted: 1962
Description

Write a program to convert numbers in one base to numbers in a second base.
There are 62 different digits: 
{ 0-9,A-Z,a-z } 
HINT: If you make a sequence of base conversions using the output of one
conversion as the input to the next, when you get back to the original base,
you should get the original number.

Input
The first line of input contains a single positive integer. This is the number
of lines that follow. Each of the following lines will have a (decimal) input
base followed by a (decimal) output base followed by a number expressed in
the input base. Both the input base and the output base will be in the range
from 2 to 62.
That is (in decimal) A = 10, B = 11, ..., Z = 35, a = 36,
b = 37, ..., z = 61 (0-9 have their usual meanings).

Output
The output of the program should consist of three lines of output for each
base conversion performed. The first line should be the input base in decimal
followed by a space then the input number (as given expressed in the input
base). The second output line should be the output base followed by a space
then the input number (as expressed in the output base). The third output
line is blank.

Sample Input
8
62 2 abcdefghiz
10 16 1234567890123456789012345678901234567890
16 35 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
35 23 333YMHOUE8JPLT7OX6K9FYCQ8A
23 49 946B9AA02MI37E3D3MMJ4G7BL2F05
49 61 1VbDkSIMJL3JjRgAdlUfcaWj
61 5 dl9MDSWqwHjDnToKcsWE1S
5 10 42104444441001414401221302402201233340311104212022133030

Sample Output
62 abcdefghiz
2 11011100000100010111110010010110011111001001100011010010001

10 1234567890123456789012345678901234567890
16 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2

16 3A0C92075C0DBF3B8ACBC5F96CE3F0AD2
35 333YMHOUE8JPLT7OX6K9FYCQ8A

35 333YMHOUE8JPLT7OX6K9FYCQ8A
23 946B9AA02MI37E3D3MMJ4G7BL2F05

23 946B9AA02MI37E3D3MMJ4G7BL2F05
49 1VbDkSIMJL3JjRgAdlUfcaWj

49 1VbDkSIMJL3JjRgAdlUfcaWj
61 dl9MDSWqwHjDnToKcsWE1S

61 dl9MDSWqwHjDnToKcsWE1S
5 42104444441001414401221302402201233340311104212022133030

5 42104444441001414401221302402201233340311104212022133030
10 1234567890123456789012345678901234567890
*/

然后就是spoj上的tutorial上的429  Simple Numbers Conversion,避免了数组的移动,代码如下:
/*************************************************************************
	> File Name: 429.cpp
	> Author: gwq
	> Mail: gwq5210@qq.com 
	> Created Time: 2014年11月07日 星期五 23时54分08秒
 ************************************************************************/

#include <cmath>
#include <ctime>
#include <cctype>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include <algorithm>

#define INF (INT_MAX / 10)
#define clr(arr, val) memset(arr, val, sizeof(arr))
#define pb push_back
#define sz(a) ((int)(a).size())

using namespace std;
typedef set<int> si;
typedef vector<int> vi;
typedef map<int, int> mii;
typedef long long ll;

const double esp = 1e-5;

//给的数的十进制范围是小于10^1000,也就是1000位,
//那么变成二进制,最长就是3000多位,开4000足够了
#define N 4010

int r, s;		//将r进制的数变成s进制
int lnum, lans;		//对应数组的长度
char str[N];		//以r进制表示的数的字符串
int num[N];		//以r进制表示的数的整数
char ans[N];		//保存以s进制的最后结果
char mp[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

int main(int argc, char *argv[])
{
	int t;
	scanf("%d", &t);
	while (t--) {
		scanf("%s%d%d", str, &r, &s);
		lnum = strlen(str);
		//将字符变成对应的整数
		for (int i = 0; i < lnum; ++i) {
			num[i] = strchr(mp, str[i]) - mp;
		}
		//商的起始位置
		int pos = 0;
		lans = 0;
		clr(ans, 0);
		//每次迭代不移动数组,节约时间
		while (pos < lnum) {
			int rem = 0;
			for (int i = pos; i < lnum; ++i) {
				num[i] += rem * r;
				rem = num[i] % s;
				num[i] /= s;
			}
			ans[lans++] = mp[rem];
			//去掉商的前导0
			while (pos < lnum && num[pos] == 0) {
				++pos;
			}
		}
		//得到的是逆序的,要逆序输出
		for (int i = lans - 1; i >= 0; --i) {
			printf("%c", ans[i]);
		}
		printf("\n");
	}

	return 0;
}

/*
spoj 429. Simple Numbers Conversion
Every integer number n is represented in positional number system
of base r by a sequence of digits 0 <= di < r, so the value is equal to:
	n = d0 + r * d1 + r2 * d2 + r3 * d3 + ...
Your task is to convert a given number in r-base represantation into
s-base representation, for example: decimal 231 into binary 11100111.
Assume that r <= 36 and the digits are 0,1,2,3,4,5,6,7,8,9, A, B, C, D,
E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z.

Input
N [the number of series <= 1000] 
n r s [n <= 101000, r,s <= 36]

Output
n [s-base representation of number n]
Text grouped in [ ] does not appear in the input and output file.

Example
Input:
3
231 10 2
ABC 15 10
XYZ 36 2

Output:
11100111
2427
1010101111111011

Test cases
There are five categories of the input data:
Test case 1: (1 pt), r = 2 and s = 10, or conversely, n<=10^9, N = 100,
Test case 2: (1 pt), 2 <= r,s <= 10, n<=10^9, N = 1000,
Test case 3: (1 pt), 2 <= r,s <= 32, n<=10^9, N = 1000,
Test case 4: (3 pts), 2 <= r,s <= 10, n<=10^1000, N = 1000,
Test case 5: (4 pts), 2 <= r,s <= 32, n<=10^1000, N = 1000.
*/

参考:
1)http://blog.csdn.net/rappy/article/details/1737671
2)http://blog.csdn.net/sjf0115/article/details/8690581
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值