# [a020 身分證檢驗](https://zerojudge.tw/ShowProblem?problemid=a020) ## Problem Description 我國的身分證字號有底下這樣的規則,因此對於任意輸入的身分證字號可以有一些基本的判斷原則,請您來判斷一個身分證字號是否是正常的號碼(不代表確有此號、此人)。 (1) 英文代號以下表轉換成數字 A=10 台北市 J=18 新竹縣 S=26 高雄縣 B=11 台中市 K=19 苗栗縣 T=27 屏東縣 C=12 基隆市 L=20 台中縣 U=28 花蓮縣 D=13 台南市 M=21 南投縣 V=29 台東縣 E=14 高雄市 N=22 彰化縣 W=32 金門縣 F=15 台北縣 O=35 新竹市 X=30 澎湖縣 G=16 宜蘭縣 P=23 雲林縣 Y=31 陽明山 H=17 桃園縣 Q=24 嘉義縣 Z=33 連江縣 I=34 嘉義市 R=25 台南縣 (2) 英文轉成的數字, 個位數乘9再加上十位數的數字 (3) 各數字從右到左依次乘1、2、3、4....8 (4) 求出(2),(3) 及最後一碼的和 (5) (4)除$10$ 若整除,則為 real,否則為 fake 例: T112663836 2 + 7*9 + 1*8 + 1*7 + 2*6 + 6*5 + 6*4 + 3*3 + 8*2 + 3*1 + 6 = 180 除以 10 整除,因此為 real ##### 輸入說明 輸入共一行。每一行包含一組身分證號碼 ##### 輸出說明 每讀入一行身分證字號,輸出 real or fake ##### 範例輸入/輸出 | 輸入 | 輸出 | | -------- | ------------ | | `T112663836` | `real` | | `S154287863` | `fake` | --- ## 解題思路 這題需要用到[字元與字串](https://hackmd.io/@letscoding/Bk543geFw),如果不熟悉的同學可以先讀完筆記再來實作。 ### 輸入 這題的輸入包含英文與數字,又要將每一位數個別處理計算,因此以字串讀入是較好選擇。若對字串處理不熟悉,可參考[字串與字元陣列](https://hackmd.io/@letscoding/Bk543geFw#%E5%AD%97%E4%B8%B2%E8%88%87%E5%AD%97%E5%85%83%E9%99%A3%E5%88%97)。 ### 處理與輸出 輸入字串後,要針對各字元進行處理,並將結果進行加總。可以分為**數字處理**與**字母處理** - 數字處理較為簡單,只要透過`c-'0'`轉換為數字再針對所在位數乘上數字即可。 - 較麻煩的部分是開頭的**字母處理**,如果 `A~Z` 有"依序"對應到 $10$~$35$,那或許可以透過 `c-'A'+10` 換算而得。但很不幸的並不是,但我們仍然不用透過26個`if - else if - else`或是`switch case`處理。我們依然可以透過以下兩個方法簡化程式。 #### 分段處理-尋找有限的規律 雖然沒有完全按照順序,但我們還是可以發現其中有一部分的對應表有照順序。我們可以分段處理: | 字母 | 數字 | 字母 | 數字 | |:----:|:-----:|:----:|:-----:| | A-H | 10~17 | P-V | 23~29 | | I | 34 | W | 32 | | J-N | 18~22 | X-Y | 30~31 | | O | 33 | Z | 35 | 所以我們可以透過8個`if - else if - else`簡化 #### 利用陣列建表 當對應的規則完全不按順序時,我們可以自己利用陣列建立一個對應表。初始化時依A\~Z的順序填入對應的值,再利用`c-'A'`將 A\~Z 轉換為 0\~25 後,即可在對應表中取得值。 --- ## Sample Code :::spoiler 分段處理 ```cpp= #include <iostream> #include <string> using namespace std; int main(){ //輸入字串 string s; cin >> s; //計算 int p = 0; //從英文字母找到對應數字 if(s[0] <= 'H') p = s[0]-'A' + 10; else if(s[0] == 'I') p = 34; else if(s[0] <= 'N') p = s[0]-'J' + 18; else if(s[0] == 'O') p = 35; else if(s[0] <= 'V') p = s[0]-'P' + 23; else if(s[0] == 'W') p = 32; else if(s[0] <= 'Y') p = s[0]-'X' + 30; else p = 33; int num = p/10 + (p%10)*9; for(int i = 1; i < 9; i++){ num += (s[i]-'0') * (9-i); } num += s[9]-'0'; //判斷並輸出 if(num % 10 == 0) cout << "real" << endl; else cout << "fake" << endl; } ``` ::: :::spoiler 建立對應表 ```cpp= #include <iostream> #include <string> using namespace std; int main(){ //建立A~Z對應的數字表 int table[26] = { 10, 11, 12, 13, 14, 15, 16, 17, 34, 18, 19, 20, 21, 22, 35, 23, 24, 25, 26, 27, 28, 29, 32, 30, 31, 33 }; //輸入字串 string s; cin >> s; //計算 int p = table[s[0]-'A']; //從英文字母找到對應數字 int num = p/10 + (p%10)*9; for(int i = 1; i < 9; i++){ num += (s[i]-'0') * (9-i); } num += s[9]-'0'; //判斷並輸出 if(num % 10 == 0) cout << "real" << endl; else cout << "fake" << endl; } ``` ::: --- ## 進階挑戰 學會了嘛?趕快試試看類似題-[a054_電話客服中心](https://zerojudge.tw/ShowProblem?problemid=a054)