例24 數制轉換
題目描述
請你編一程序實現兩種不同進制之間的數據轉換。
輸入格式
共三行,第一行是一個正整數,表示需要轉換的數的進制n(2≤n≤16),第二行是一個n進制數,若n>10則用大寫字母A-F表示數碼10-15,並且該n進制數對應的十進制的值不超過1000000000,第三行也是一個正整數,表示轉換之后的數的進制m(2≤m≤16)。
輸出格式
一個正整數,表示轉換之后的m進制數。
輸入樣例
16
FF
2
輸出樣例
11111111
(1)編程思路。
十進制整數轉換為R進制整數的基本方法是:“除R取余”。具體做法為:對於十進制數整數,用R連續除要轉換的十進制整數及各次所得之商,直除到商等於0時為止,則各次所得之余數即為所求R進制整數由低位到高位的值。這個過程可以寫成一個簡單的循環。
一般而言,對於任意的R進制數 An-1An-2…A1A0可以表示為以下和式:
An-1×Rn-1 +…+A1×R1+A0×R0 (其中R為基數)
這個和式也稱為“按權展開式”。
R進制數轉換為十進制數的基本方法是將R進制數的各位按權展開相加即可。
本例的思路是:將輸入的n進制整數按權值展開后轉換為十進制整數,再將所得的十進制整數采用“除m取余”轉換為m進制整數即可。
(2)源程序。
#include <stdio.h>
int main()
{
char table[17]="0123456789ABCDEF";
int n,m;
char s[33];
scanf("%d",&n);
scanf("%s",s);
scanf("%d",&m);
int num=0;
for (int i=0;s[i]!='\0';i++) // n進制整數按權值展開后轉換為十進制整數num
{
if (s[i]>='0' && s[i]<='9')
num=num*n+s[i]-'0';
else
num=num*n+s[i]-'A'+10;
}
int digit[32],cnt=0;
do { // 十進制整數采用“除m取余”轉換為m進制整數
digit[cnt++]=num%m;
num=num/m;
} while (num!=0);
for (int i=cnt-1;i>=0;i--)
printf("%c",table[digit[i]]);
printf("\n");
return 0;
}
習題24
24-1 高低位交換
本題選自洛谷題庫 (https://www.luogu.org/problem/P1100)。
題目描述
給出一個小於232的正整數。這個數可以用一個32位的二進制數表示(不足32位用0補足)。我們稱這個二進制數的前16位為“高位”,后16位為“低位”。將它的高低位交換,我們可以得到一個新的數。試問這個新的數是多少(用十進制表示)。
例如,數1314520用二進制表示為00000000000101000000111011011000(添加了11個前導0補足為32位),其中前16位為高位,即0000000000010100;后16位為低位,即0000111011011000。將它的高低位進行交換,我們得到了一個新的二進制數00001110110110000000000000010100。它即是十進制的249036820。
輸入格式
一個小於232的正整數。
輸出格式
將新的數輸出
輸入樣例
1314520
輸出樣例
249036820
(1)編程思路。
將輸入的十進制整數采用“除2取余”轉換為二進制整數,再將所得的二進制整數依照低16位在前高16位在后的方式按權值展開后轉換為十進制整數即可。
(2)源程序。
#include <stdio.h>
int main()
{
unsigned int num;
scanf("%d",&num);
int digit[32]={0},cnt=0;
do { // 十進制整數采用“除2取余”轉換為二進制整數
digit[cnt++]=num%2;
num=num/2;
} while (num!=0);
int i;
for (i=15;i>=0;i--) // 先將低16位按權值展開
num=num*2+digit[i];
for (i=31;i>=16;i--) // 再將高16位按權值展開
num=num*2+digit[i];
printf("%u\n",num);
return 0;
}
24-2 進制轉換
本題選自洛谷題庫 (https://www.luogu.org/problem/P1017)。
題目描述
一般說來,任何一個正整數R或一個負整數-R都可以被選來作為一個數制系統的基數。如果是以R或-R為基數,則需要用到的數碼為 0,1,....R-1。例如,當R=7時,所需用到的數碼是0,1,2,3,4,5和6,這與其是R或-R無關。如果作為基數的數絕對值超過10,則為了表示這些數碼,通常使用英文字母來表示那些大於9的數碼。例如對16進制數來說,用A表示10,用B表示11,用C表示12,用D表示13,用E表示14,用F表示15。
在負進制數中是用-R作為基數,例如-15 (十進制)相當於110001 (-2進制),並且它可以被表示為2的冪級數的和數:
110001=1×(−2)5 +1×(−2)4 +0×(−2)3 +0×(−2)2 +0×(−2)1 +1×(−2)0。
設計一個程序,讀入一個十進制數和一個負進制數的基數,並將此十進制數轉換為此負進制下的數:-R∈{-2,-3,-4,...,-20}
輸入格式
輸入包括多組測試數據。
每組測試數據占一行,每行有兩個輸入數據。
第一個是十進制數N (−32768≤N≤32767)
第二個是負進制數的基數-R。
輸出格式
結果顯示在屏幕上,相對於輸入,應輸出此負進制數及其基數,若此基數超過10,則參照16進制的方式處理。
輸入樣例
30000 -2
-20000 -2
28800 -16
-25000 -16
輸出樣例
30000=11011010101110000(base-2)
-20000=1111011000100000(base-2)
28800=19180(base-16)
-25000=7FB8(base-16)
(1)編程思路。
我們知道十進制整數是采用“除R取余”轉換為R進制整數的,對於負基數(-R)同樣如此。
先看問題描述中的例子-15轉換為-2進制數。在C語言中“除R取余”運算過程為:
被除數 |
除數 |
商 |
余數 |
-15 |
-2 |
7 |
-1 |
7 |
-2 |
-3 |
1 |
-3 |
-2 |
1 |
-1 |
1 |
-2 |
0 |
1 |
這里余數出現了負數,需要將余數轉換成正數,怎么處理呢?
因為, 被除數=商*除數+余數=商*除數+除數+余數-除數=(商+1)*除數+(余數-除數)。因此,若余數為負時,只需要將商+1,余數-除數,就可以將余數轉換為正數。
被除數 |
除數 |
商 |
余數 |
校正的商 |
校正余數 |
-15 |
-2 |
7 |
-1 |
8 |
1 |
8 |
-2 |
-4 |
0 |
|
|
-4 |
-2 |
2 |
0 |
|
|
2 |
-2 |
-1 |
0 |
|
|
-1 |
-2 |
0 |
-1 |
1 |
1 |
1 |
-2 |
0 |
1 |
|
|
因此,-15轉換為-2進制數為110001。
(2)源程序。
#include <stdio.h>
int main()
{
char table[21]="0123456789ABCDEFGHIJ";
int num,r;
while (scanf("%d%d",&num,&r)!=EOF)
{
printf("%d=",num);
int digit[32]={0},cnt=0;
do {
int t=num%r;
num=num/r;
if (t<0) {t=t-r; num++; }
digit[cnt++]=t;
} while (num!=0);
int i;
for (i=cnt-1;i>=0;i--)
printf("%c",table[digit[i]]);
printf("(base%d)\n",r);
}
return 0;
}
24-3 彩燈和按鈕
問題描述
有一個機器,它有 m(2≤m≤30) 個彩燈和一個按鈕。每按下按鈕時,最右邊的彩燈會發生一次變換。變換為:①如果當前狀態為紅色,它將變成綠色;②如果當前狀態為綠色,它將變成藍色;③如果當前狀態為藍色,它將變成紅色,並且它左邊的彩燈(如果存在)也會發生一次變換。初始狀態下所有的彩燈都是紅色的。
輸入格式
輸入包括多組測試,第1行給出正整數T(1≤T≤15),表示測試數據的組數。
每組測試數據包括兩個整數m(2≤m≤30) 和 n(1≤n<263),分別表示彩燈的個數和按鈕被按下的次數。
輸出格式
對每組測試數據,按從左到右的順序輸出各彩燈的顏色狀態,R 表示紅色,G表示綠色,B 表示藍色。
輸入樣例
2
3 1
2 3
輸出樣例
RRG
GR
(1)編程思路。
每按一次按鈕都是從最右邊的彩燈開始變換,將m個彩燈可以看成一個m位數,最右邊的彩燈為其個位數,每按一次按鈕可以看成個位數加1。每按三次后從右數第二個彩燈才會變一下,每按9次從右邊數第三個彩燈會變換一次,……。
顯然,m個彩燈表示的m位數可以看成是一個三進制數,紅燈表示0,綠燈表示1,藍燈表示2,將輸入的n轉換為3進制數即可。
(2)源程序。
#include <stdio.h>
#include <string.h>
int main()
{
char table[4]="RGB";
int t, m, len;
long long n;
int color[30];
scanf("%d", &t);
while(t--)
{
memset(color, 0, sizeof(color));
scanf("%d%lld", &m, &n);
len = m;
while (n > 0 && m > 0)
{
color[--m] = n % 3;
n /= 3;
}
for (int i=0; i<len; i++)
putchar(table[color[i]]);
putchar('\n');
}
return 0;
}