常用模板
大整數的存儲
模板如下:
struct bign // 大整數存儲結構體
{
int len; // 記錄大整數的位數
int d[1000]; // 存儲每一位的數的數組
bign() // 構造函數,用於初始化
{
memset(d, 0,sizeof(d));
len = 0;
}
};
bign Change(char str[]) // 將輸入的大整數字符串轉換爲bign
{
bign a;
a.len = strlen(str); // bign的長度就是字符串的長度
for (int i = 0; i < a.len; ++i)
a.d[i] = str[a.len - i - 1] - '0'; // 逆着賦值
return a;
}
大整數的比較
模板如下:
int Compare(bign a, bign b)
{
if (a.len > b.len)
return 1;
else if (a.len < b.len)
return -1;
else
{
for (int i = a.len - 1; i >= 0; --i)
{
if (a.d[i] > b.d[i]) // 只有要一位a大,則a大
return 1;
else // 只要有一位a小,則a小
return -1;
}
}
return 0; // 兩數相等
}
大整數的四則運算
高精度加法a + b
如果a、b都是非負數可直接應用該模板。如果有一個是負數,則去掉負號採用高精度減法模板。如果都是負數,則去掉負號,應用該模板後添加一個負號。
bign Add(bign a, bign b) // 高精度a + b
{
bign c;
int carry = 0; // carry是進位
for (int i = 0; i < a.len || i < b.len; ++i) // 以較長的位爲界限
{
int temp = a.d[i] + b.d[i] + carry; // 兩個對應位與進位相加
c.d[c.len++] = temp % 10; // 個位數爲該位結果
carry = temp / 10; // 十位數爲新的進位
}
if (carry) // 如果最後進位不爲0,則直接賦給結果的最高位
c.d[c.len++] = carry;
return c;
}
高精度減法a - b
使用該模板前需要比較兩個數的大小,如果被減數小於減數,即a < b,需要交換兩個變量,然後輸出負號,再使用模板。
bign Sub(bign a, bign b) // 高精度a - b
{
bign c;
for (int i = 0; i < a.len || i < b.len; ++i) // 以較長的位爲界限
{
if (a.d[i] < b.d[i]) // 如果不夠減
{
--a.d[i + 1]; // 向高位借位,即高位減1
a.d[i] += 10;
}
c.d[c.len++] = a.d[i] - b.d[i]; // 減法結果爲當前位結果
}
while (c.len - 1 >= 1 && c.d[c.len - 1] == 0)
--c.len; // 去除高位的0,即將位數減小,同時保留至少一位最低位
return c;
}
高精度與低精度的乘法a * b
如果a和b中存在負數,先記錄下其負號,應用模板後添加負號。
bign Multi(bign a, int b) // 高精度a * b
{
bign c;
int carry = 0; // carry是進位
for (int i = 0; i < a.len; ++i)
{
int temp = a.d[i] * b + carry;
c.d[c.len++] = temp % 10; // 個位數爲該位結果
carry = temp / 10; // 高位部分作爲新的進位
}
while (carry) // 和加法不一樣,乘法的進位可能不止一位,因此用while
{
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
高精度與低精度的除法a / b
考慮到函數每次只能返回一個數據,而很多題目裏會經常要求得到餘數,因此把餘數寫成引用的形式直接作爲參數傳入,或是把r設成全局變量。
bign Divide(bign a, int b, int &r) // 高精度a / b
{
bign c;
c.len = a.len; // 被除數的每一位和商的每一位是一一對應的,因此先令長度相等
for (int i = a.len - 1; i >= 0; --i) // 從高位開始做除法
{
r = r * 10 + a.d[i]; // 和上一位遺留的餘數組合
if (r < b)
c.d[i] = 0; // 不夠除,該位爲0
else // 夠除
{
c.d[i] = r / b; // 商
r = r % b; // 獲得新的餘數
}
}
while (c.len - 1 >= 1 && c.d[c.len - 1] == 0)
--c.len; // 去除高位的0,即將位數減小,同時保留至少一位最低位
return c;
}
高精度除法應用於進制轉換
模板如下:
bign Divide(bign a, int b, int &r, int m) // 在m進制中做a / b
{
bign c;
c.len = a.len;
for (int i = a.len - 1; i >= 0; --i)
{
r = r * m + a.d[i];
if (r < b)
c.d[i] = 0;
else
{
c.d[i] = r / b;
r = r % b;
}
}
while (c.len - 1 >= 1 && c.d[c.len - 1] == 0)
--c.len;
return c;
}
問題 A: a+b
題目描述
實現一個加法器,使其能夠輸出a+b的值。
輸入
輸入包括兩個數a和b,其中a和b的位數不超過1000位。
輸出
可能有多組測試數據,對於每組數據,
輸出a+b的值。
樣例輸入
6 8
2000000000 30000000000000000000
樣例輸出
14
30000000002000000000
Note
這一題足足改了我一個多小時,這麼簡單的一道題目,你們一定不會相信一直不AC的原因居然在結構體定義中,構造函數裏面的順序可以隨便,但是len的定義必須放在數組前面!!否則一定會出錯!!我真的!!!無語了,什麼OJ系統!!!
#include <cstdio>
#include <cstring>
using namespace std;
struct bign // 大整數存儲結構體
{
int len; // 記錄大整數的位數
int d[1000]; // 存儲每一位的數的數組
bign() // 構造函數,用於初始化
{
memset(d, 0, sizeof(d));
len = 0;
}
};
bign Change(char str[]) // 將輸入的大整數字符串轉換爲bign
{
bign a;
a.len = strlen(str); // bign的長度就是字符串的長度
for (int i = 0; i < a.len; ++i)
a.d[i] = str[a.len - i - 1] - '0'; // 逆着賦值
return a;
}
bign Add(bign a, bign b) // 高精度a + b
{
bign c;
int carry = 0; // carry是進位
for (int i = 0; i < a.len || i < b.len; ++i) // 以較長的位爲界限
{
int temp = a.d[i] + b.d[i] + carry; // 兩個對應位與進位相加
c.d[c.len++] = temp % 10; // 個位數爲該位結果
carry = temp / 10; // 十位數爲新的進位
}
if (carry != 0) // 如果最後進位不爲0,則直接賦給結果的最高位
c.d[c.len++] = carry;
return c;
}
int main()
{
char A[1010], B[1010];
while (scanf("%s %s", A, B) != EOF)
{
bign a = Change(A);
bign b = Change(B);
bign c = Add(a, b);
for (int i = c.len - 1; i >= 0; --i)
printf("%d", c.d[i]);
printf("\n");
}
return 0;
}
問題 B: N的階乘
題目描述
輸入一個正整數N,輸出N的階乘。
輸入
正整數N(0<=N<=1000)
輸出
輸入可能包括多組數據,對於每一組輸入數據,輸出N的階乘
樣例輸入
0
4
7
樣例輸出
1
24
5040
Note
位數數組開大點。
#include <iostream>
#include <cstring>
using namespace std;
struct bign // 大整數存儲結構體
{
int len; // 記錄大整數的位數
int d[100000]; // 存儲每一位的數的數組
bign() // 構造函數,用於初始化
{
memset(d, 0,sizeof(d));
len = 0;
}
};
bign Multi(bign a, int b) // 高精度a * b
{
bign c;
int carry = 0; // carry是進位
for (int i = 0; i < a.len; ++i)
{
int temp = a.d[i] * b + carry;
c.d[c.len++] = temp % 10; // 個位數爲該位結果
carry = temp / 10; // 高位部分作爲新的進位
}
while (carry) // 和加法不一樣,乘法的進位可能不止一位,因此用while
{
c.d[c.len++] = carry % 10;
carry /= 10;
}
return c;
}
int main()
{
int n;
while (cin >> n)
{
bign a;
a.len = 1; // 初始化
a.d[0] = 1;
for (int i = 2; i <= n; ++i)
a = Multi(a, i);
for (int i = a.len - 1; i >= 0; --i)
cout << a.d[i];
cout << endl;
}
return 0;
}
問題 C: 浮點數加法
題目描述
求2個浮點數相加的和
題目中輸入輸出中出現浮點數都有如下的形式:
P1P2…Pi.Q1Q2…Qj
對於整數部分,P1P2…Pi是一個非負整數
對於小數部分,Qj不等於0
輸入
對於每組案例,第1行是測試數據的組數n,每組測試數據佔2行,分別是兩個加數。
每組測試數據之間有一個空行,每行數據不超過100個字符
輸出
每組案例是n行,每組測試數據有一行輸出是相應的和。
輸出保證一定是一個小數部分不爲0的浮點數
樣例輸入
2
3.756
90.564
4543.5435
43.25
樣例輸出
94.32
4586.7935
Note
修修改改了一天,終於寫出了一個較爲簡潔且易懂的代碼,基本思路是類比浮點數的相加。用兩個更大的字符串存儲a,b,但是要小數點對齊的存進去。注意,更大的字符串長度至少要分配200,因爲存在兩個極端情況:整數98位小數一位或者整數一位小數98位,這樣對齊後相加的結果需要一個至少201(想想爲什麼是201)長度的字符串來存儲。具體實現看下面:
- 用p1、p2分別標記a、b的小數點位置。
- 定義三個運算字符串用於模擬加法,stc存儲運算的結果。別忘記初始化,加小數點以及加上終止符,要不然會出錯。字符串的處理一定要謹慎,另外就是一定要把終止符算入長度內!
- 將a和b按照小數點地位置存入sta和stb。拿a和sta舉個例子,具體的做法就是,從小數點開始,用 i 和 j 往左右遍歷a,用ql和qr往左右遍歷sta,然後將a[i]或a[j]存入相應的sta[ql]或sta[qr]。
- 進行正常的相加操作。
- 計算後的結果很有可能頭尾存在大量的0,所以需要過濾0字符。用兩個while來標記第一個非0字符和最後一個非0字符的下標。然後輸出兩個下標內的所有字符即可。
#include <iostream>
#include <cstring>
using namespace std;
void F(int p, char s[], char a[]) // 將字符串a放到字符串s中間
{
for (int i = p - 1, j = p + 1, ql = 123, qr = 125;;)
{
if (i >= 0)
s[ql--] = a[i--];
if (j < strlen(a))
s[qr++] = a[j++];
if (i < 0 && j >= strlen(a))
break;
}
}
int main()
{
int n;
char a[110], b[110];
while (cin >> n) // 輸入案例數
{
while (n--)
{
cin >> a >> b; // 輸入兩個浮點數字符串
int p1 = 0, p2 = 0; // p1,p2分別標記小數點的下標
while (a[p1++] != '.');
while (b[p2++] != '.');
char sta[251], stb[251], stc[251];
for (int i = 0; i < 250; ++i) // 初始化運算字符串
sta[i] = stb[i] = stc[i] = '0';
sta[124] = stb[124] = stc[124] = '.'; // 標記小數點的位置
sta[250] = stb[250] = stc[250] = '\0'; // 給字符串加上終止符
F(p1 - 1, sta, a);
F(p2 - 1, stb, b);
int carry = 0; // 進位
for (int i = strlen(sta) - 1; i >= 0; --i) // 進行加法運算
{
if (sta[i] != '.')
{
int temp = (sta[i] - '0') + (stb[i] - '0') + carry;
stc[i] = (temp % 10) + '0';
carry = temp / 10;
}
}
int head = 0, tail = strlen(stc) - 1; // flag = 1表明該0是首尾的無效0而不是浮點數中的0
while (stc[head++] == '0');
while (stc[tail--] == '0');
for (int i = head - 1; i <= tail + 1; ++i)
cout << stc[i];
cout << endl;
}
}
return 0;
}
問題 D: 進制轉換
題目描述
將M進制的數X轉換爲N進制的數輸出。
輸入
輸入的第一行包括兩個整數:M和N(2<=M,N<=36)。
下面的一行輸入一個數X,X是M進制的數,現在要求你將M進制的數X轉換成N進制的數輸出。
輸出
輸出X的N進製表示的數。
樣例輸入
10 2
11
樣例輸出
1011
提示
注意輸入時如有字母,則字母爲大寫,輸出時如有字母,則字母爲小寫。
Note
可以先做問題F再來做這一題就很好理解了。
#include <iostream>
#include <cstring>
using namespace std;
struct bign
{
int len;
int d[1000];
bign()
{
memset(d, 0, sizeof(d));
len = 0;
}
};
bign Change(char str[])
{
bign a;
a.len = strlen(str);
for (int i = 0; i < a.len; ++i)
{
if (str[a.len - i - 1] <= '9')
a.d[i] = str[a.len - i - 1] - '0';
else if (str[a.len - i - 1] >= 'A')
a.d[i] = str[a.len - i - 1] - 'A' + 10;
}
return a;
}
bign Divide(bign a, int b, int &r, int m) // 在m進制中做a / b
{
bign c;
c.len = a.len;
for (int i = a.len - 1; i >= 0; --i)
{
r = r * m + a.d[i];
if (r < b)
c.d[i] = 0;
else
{
c.d[i] = r / b;
r = r % b;
}
}
while (c.len - 1 >= 1 && c.d[c.len - 1] == 0)
--c.len;
return c;
}
int main()
{
int m, n;
char num[1000], ans[1000];
while (cin >> m >> n)
{
cin >> num;
int len = 0, r;
bign a = Change(num);
do {
r = 0;
a = Divide(a, n, r, m); // 在m進制內做除法
if (r >= 0 && r <= 9)
ans[len++] = r + '0';
else if (r >= 10)
ans[len++] = r - 10 + 'a';
} while (!(a.len == 1 && a.d[0] == 0)); // 當ans爲0時終止循環
ans[len] = '\0'; // 加上終止符
for (int i = len - 1; i >= 0; --i)
cout << ans[i];
cout << endl;
}
return 0;
}
問題 E: 大整數排序
題目描述
對N個長度最長可達到1000的數進行排序。
輸入
輸入第一行爲一個整數N,(1<=N<=100)。
接下來的N行每行有一個數,數的長度範圍爲1<=len<=1000。
每個數都是一個正數,並且保證不包含前綴零。
輸出
可能有多組測試數據,對於每組數據,將給出的N個數從小到大進行排序,輸出排序後的結果,每個數佔一行。
樣例輸入
4
123
1234
12345
2345
樣例輸出
123
1234
2345
12345
Note
簡單的寫個cmp排序就好了。長度越長自然越大,長度相等時就比較字符串的大小。另外需要注意的是,sort函數是不能直接對二維字符串數組進行排序的。所以不要定義成num[100][1010]然後對num進行排序,在C++中無法編譯通過。具體的可參考這篇博客:sort對二維字符數組排序。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
struct number
{
char data[1010];
} num[100];
bool cmp(number a, number b)
{
if (strlen(a.data) != strlen(b.data))
return strlen(a.data) < strlen(b.data);
else
return strcmp(a.data, b.data) < 0;
}
int main()
{
int n;
while (cin >> n)
{
for (int i = 0; i < n; ++i)
cin >> num[i].data;
sort(num, num + n, cmp);
for (int i = 0; i < n; ++i)
cout << num[i].data << endl;
}
return 0;
}
問題 F: 10進制 VS 2進制
題目描述
對於一個十進制數A,將A轉換爲二進制數,然後按位逆序排列,再轉換爲十進制數B,我們稱B爲A的二進制逆序數。
例如對於十進制數173,它的二進制形式爲10101101,逆序排列得到10110101,其十進制數爲181,181即爲173的二進制逆序數。
輸入
一個1000位(即10^999)以內的十進制數。
輸出
輸入的十進制數的二進制逆序數。
樣例輸入
985
樣例輸出
623
Note
這一題是大整數的計算,如果使用權值計算二進制轉十進制,就需要引入大整數的乘法的加法,會非常的麻煩。所以我們需要換方法計算。首先十進制轉二進制的方法很常規,就是不停地除以2,將得到的餘數記錄下來。類比過來,二進制轉十進制也是可以這麼做的。二進制轉十進制,是將二進制數x不停地除以10直到x等於0。將每一步的餘數按照二進制轉換爲十進制後存入數組,逆序輸出即爲二進制所對應的十進制數。具體的請看如下所示圖片:
除法中有幾個小細節是需要注意的:首先因爲是二進制除法,所以上商最高只能上1,其次就是二進制數x除以某數y,其實是除以y的二進制,所以是1010,;最後就是借位只能借2而不是10。
圖中的結果最後轉換爲的十進制即是。有不懂的地方可以在評論區提出來。但是呢,這只是很簡單的商 x 除數 + 餘數 = 被除數。我們需要的是餘數,按照上面的思路,我們繼續往下做就能得到111110 / 1010 = 110…10,然後是110 / 1010 = 110。逆序後輸出的結果就是’110’ ‘10’ '11’即623.
最後就需要對大整數除法加以改進了。因爲是二進制除法,所以每一次向前借位只能借2,而且往往一位是不夠借的而是循環借好幾位才能湊出一位來。因此我們可對大整數除法的模板進行改進,添加一個進制的參數即可實現任意進制轉任意進制。
還有一點需要提醒的是,儘管模擬的是二進制的除法,但是實際上觀察Divide函數可以知道,我們的比較仍然是基於十進制的比較。這一點需要同學們思考一下了。
#include <iostream>
#include <cstring>
using namespace std;
struct bign
{
int len;
int d[1000];
bign()
{
memset(d, 0, sizeof(d));
len = 0;
}
};
bign Change(char str[])
{
bign a;
a.len = strlen(str);
for (int i = 0; i < a.len; ++i)
a.d[i] = str[a.len - i - 1] - '0';
return a;
}
bign Divide(bign a, int b, int &r, int m) // 在m進制中做a / b
{
bign c;
c.len = a.len;
for (int i = a.len - 1; i >= 0; --i)
{
r = r * m + a.d[i];
if (r < b)
c.d[i] = 0;
else
{
c.d[i] = r / b;
r = r % b;
}
}
while (c.len - 1 >= 1 && c.d[c.len - 1] == 0)
--c.len;
return c;
}
int main()
{
char dec[1010], bin[3000];
while (cin >> dec)
{
int len = 0, r;
bign ans = Change(dec);
do {
r = 0;
ans = Divide(ans, 2, r, 10); // 在十進制內做除法,做ans / 2
bin[len++] = r + '0';
} while (!(ans.len == 1 && ans.d[0] == 0)); // 當ans爲0時終止循環
bin[len] = '\0'; // 加上終止符
ans = Change(bin);
len = 0;
do {
r = 0;
ans = Divide(ans, 10, r, 2); // 在二進制內做除法,做ans / 10
bin[len++] = r + '0';
} while (!(ans.len == 1 && ans.d[0] == 0));
bin[len] = '\0';
for (int i = len - 1; i >= 0; --i)
cout << bin[i];
cout << endl;
}
return 0;
}
一定要自己寫一遍哦~~~