[原創][luogu]P1217 迴文質數 真·生成迴文的方法

月神的使者發表於2023-04-02

不多說,直接看程式碼,都在註釋裡

// 中心思想:
// * 1. 代入資料只想迴文的一半和位數的變化
// *   例. 1001 和 101 都存的是10, 但是位數一個是4, 一個是3 
// * 2. 安裝只存一半的思想,進位時是從中心進位 
// *   例. 1001 => 1111, 101 => 111
// *   例. 99 => 101, 999 => 1001
// * 3. 進位時儲存的方式
// *   例. 偶數進位後是奇數位的情況 (實際數字:9999 | 儲存數字:99 | 位數:4) => (實際數字:10001 | 儲存數字:100 | 位數:5)
// *   例. 奇數進位後是偶數位的情況 (實際數字:99999 | 儲存數字:999 | 位數:5) => (實際數字:100001 | 儲存數字:100 | 位數:6) 
// *   例. 雖然都是進位,但是進位後的數字奇數時儲存數字會進一位,偶數時不會進位 

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>

using namespace std;

// 迴文結構體 
struct hui {
	int w; // 位數 
	int n; // 迴文的一半 奇數是w/2+1位 偶數是w/2位 如 1001為10, 101還是為 10
};

// 雙重迴圈判質數,基礎演算法,就不註釋了 
// 有人特判二的倍數,但是我覺得不必要,進迴圈第一個試的就是2,時間複雜度沒差多少 
bool isZ(int n) {
	if(n <= 1) {
		return false;
	} 
	for(int i=2; i*i<=n; i++) {
		if(n%i==0) {
			return false;
		}
	}
	return true;
}

// 獲取引數x之後的第一個迴文數 
hui getFirst(int x) {
	// 初始化 
	int a[10] = {}; // 存每一位的陣列 
	hui t; // 要返回的迴文數結構體 
	
	// log10求位數 注意!!! log10返回浮點數!!! 
	t.w = log10(x)+1;
	
	// 考慮順序地拆解每一位 例如: 10是[1, 0]不是[0, 1] 
	for(int i=1; i<=t.w; i++) {
		a[t.w-i] = x%10;
		x /= 10;
	}
	
	// 這裡是重點!!! 
	// 開始之前,先解釋一串表示式 t.w/2-1+(t.w&1)
	// 這串表示式是找到迴文的中心,偶數是靠前一點的中心
	// 位數除以二是找到中心,減一是適配陣列從0開始,(t.w&1)是用位運算判斷奇偶,奇數就返回1,偶數返回0,可以帶入幾個數試試 
	int i, j;
	// 開始 
	i = 0;
	// 結尾 
	j = t.w - 1;
	// 左右兩邊向中心走(什麼雙指標之類的 
	while(i<j) {
		// 重點中的核心!!! 
		// 如果右邊的迴文數比左邊的大就讓左邊進一
		// 例如 23042 出來的是 23100 不是24042! 
		// 13出來的是20 不是33! 
		// 因為結構體只存迴文的一半, 所以只搞好一半就行 
		if(a[i] < a[j]) {
			a[t.w/2-1+(t.w&1)]++;
			break;
		}
		i++;
		j--;
	}
	
	// 搞好陣列了就要存入結構體,例如 1001 存 10, 100001存 100,存一半 (10001也是存10但是它們結構體裡位數不一樣 
	t.n = 0;
	for(i = 0; i<t.w/2+(t.w&1); i++) {
		t.n *= 10;
		t.n += a[i];
	}
	return t;
}

// 下一個迴文數 
// 這個函式就是找規律 
void nextHui(hui& x) {
	int y = x.n;
	x.n++;
	// 是否進位
	// 看文章開頭的中心思想 
	if(int(log10(x.n))+1 != int(log10(y))+1) {
		if(x.w&1) {
			x.n /= 10;
		}
		x.w++;
	}
}

// 結構體轉int 
int toInt(hui x) {
	int s = x.n;
	int t = x.n;
	if(x.w&1) {
		t /= 10;
	}
	while(t!=0) {
		s *= 10;
		s += t%10;
		t /= 10;
	}
	return s;
}

int main() {
	int start, end;
	cin >> start >> end;
	
	hui x;
	x = getFirst(start);
	
	// 沒到就列舉 
	while(toInt(x) <= end) {
		// 判斷質數 
		if(isZ(toInt(x))) {
			cout << toInt(x) << endl;
		}
		// 下一個迴文數 
		nextHui(x);
	}
	return 0;
}

真的寫了很久,題目更是寫了一天請幫忙點個贊,這對我幫助很大

相關文章