2018年清華軟院推免考試(校外直博&校內碩/博) 第三題——同構數
問題描述
2018年清華軟院推免考試(校外直博&校內碩/博)
第三題——同構數
如果一個數n滿足:記n的位數為d(n),若n的各次冪的末d(n)位都與n相等,則稱n為“同構數”。現在題目是,輸入進位制m和正整數k,求m進位制下第k個同構數,例如10進位制下第4個同構數是25(1,5,6,25)。這裡約定所有進位制的第一個同構數都是1.
規模約定:m大於5小於16,k小於21,保證結果存在且小於INT_MAX,但不保證中間計算過程不會超出INT_MAX
示例:
Input
6 2
Output
3
(因為是考後回憶,可能記憶有些模糊了,題乾和程式碼都有可能有錯,忘諒解)
------------------------------------------------------------
思路
筆者想到的方法是高精度+動態規劃(類似的一個思路)。
注意到同構數的兩個性質:
1. 如果一個數和它的平方結尾相等,則這個數和它的各次冪的結尾都相等,因此僅憑平方就能判定這個數是同構數
2. 一個n位同構數的末(n-1), (n-2), …,2, 1位也是同構數,這個性質決定了可以採用類似動態規劃的方法大大減少計算量
所謂“類似動態規劃的方法”是:
首先計算1位的同構數,儲存在vector中;
2位的同構數的個位一定在vector中,因此只要列舉個位是vector中的數的十位數進行判斷,將2位的同構數新增到vector中;
3位的同構數要麼後兩位是2位的同構數,要麼第2位是0,個位是1位的同構數,再對符合條件的3位數列舉判斷新增;
以此類推……
其實這道題打表完全是可以的,但由於清華軟院機試不是OJ,怕老師會看程式碼,因此沒敢打表。
另外就是本題可以在第一題的程式碼上修改,需要增加的程式碼量並不大。
------------------------------------------------------------
程式碼
#define _CRT_SECURE_NO_WARNINGS //輸入兩個長度不超過200的正整數A,B,求A和B的乘積。保證輸入的正整數不會以0開頭,要求輸出的正整數也不能以0開頭 #include <cstdio> #include <cstring> #include<vector> using namespace std; class BigInteger { public: static const int maxn = 1010; static int BASE; BigInteger (void) { n = 1; memset(digit, 0, sizeof(digit)); } BigInteger (int one_digit) { n = 1; memset(digit, 0, sizeof(digit)); digit[0] = one_digit; } BigInteger &operator=(const BigInteger &rhs) { n = rhs.n; for (int i = 0; i < n; i++) { digit[i] = rhs.digit[i]; } return *this; } int digit[maxn], n; void readInput() { static char buffer[maxn]; scanf("%s", buffer); int len = strlen(buffer); n = 0; for (int i = len - 1; i >= 0; i--) { digit[n++] = buffer[i] - '0'; } } void normalize(int i) { n = i; while (n > 1 && digit[n - 1] == 0) { n--; } } void clear0() { n = 1; memset(digit, 0, sizeof(digit)); } void add(const BigInteger &rhs, BigInteger &result) const{ int i, si = 0; result.clear0(); for (i = 0; i < n + rhs.n; i++) { result.digit[i] = si + digit[i] + rhs.digit[i]; result.digit[i+1] = result.digit[i] / BASE; result.digit[i] = result.digit[i] % BASE; } result.normalize(n+rhs.n); } void sub(const BigInteger &rhs, BigInteger &result) const {//ensure *this >= rhs int i, x = 0; for (i = 0; i < n; i++) { x += digit[i]; if (i < rhs.n) { x -= rhs.digit[i]; } bool borrow = false; if (x < 0) { x += BASE; borrow = true; } result.digit[i] = x; if (borrow) { x = -1; } else { x = 0; } } result.normalize(i); } void mul(const BigInteger &rhs, BigInteger &result) const { for (int i = 0; i < n + rhs.n; i++) { result.digit[i] = 0; } for (int i = 0; i < n; i++) { for (int j = 0; j < rhs.n; j++) { result.digit[i + j] += digit[i] * rhs.digit[j]; if (result.digit[i + j] >= BASE) { result.digit[i + j + 1] += result.digit[i + j] / BASE; result.digit[i + j] %= BASE; } } } result.normalize(n + rhs.n); } void left_shift(int L) { int i; for (i=n-1; i>=0; i--) { digit[i+L] = digit[i]; } for (i=L-1; i>=0; i--) { digit[i] = 0; } n += L; } bool tail (const BigInteger &rhs) const { // ensure rhs >= *this for (int i = 0; i < n; i++) { if (digit[i] != rhs.digit[i]) { return false; } } return true; } int compare(const BigInteger &rhs) const { if (n != rhs.n) { return n - rhs.n; } for (int i = n - 1; i >= 0; i--) { if (digit[i] != rhs.digit[i]) { return digit[i] - rhs.digit[i]; } } return 0; } void output() const { bool started = false; for (int i = n - 1; i >= 0; i--) { putchar('0' + digit[i]); } putchar('\n'); } int toBASE10() // switch to integer of BASE 10 { int ret = 0, i, multiplier = 1; for (i=0; i<n; i++) { ret += digit[i] * multiplier; multiplier *= 10; } return ret; } }; int BigInteger::BASE; int main() { int m = 10, k = 4, cnt = 0, i = 0, j = 1; scanf("%d%d", &m, &k); BigInteger::BASE = m; BigInteger A, B, C; vector<BigInteger> v; do { if (i == 0) { for (j=1; j<m; j++) { A = BigInteger(j); A.mul(A, C); if (A.tail(C)) { cnt++; v.push_back(A); } if (cnt == k) { goto mark; } } i++; } else { vector<BigInteger> pre(v); for (j = 1; j<m; j++) { B = BigInteger(j); B.left_shift(i); for (vector<BigInteger>::iterator it = pre.begin(); it != pre.end(); it++) { B.add(*it, A); A.mul(A, C); if (A.tail(C)) { cnt++; v.push_back(A); } if (cnt == k) { goto mark; } } } i++; } }while (cnt < k); mark: printf("%d", A.toBASE10()); return 0; }