1. 程式人生 > >2018年清華軟院推免考試(校外直博&校內碩/博) 第三題——同構數

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;
}