內容
段考後的延平:
https://drive.google.com/file/d/1j4qESZGdI0jQW6uuxcSb7tfNsq_SB_vl/view?usp=sharing
XD
輸入
一行一個非負整數 n(n<2^31)
1
2
3
4
輸出
輸出此整數在二進位中 1 的個數
1
1
2
1
解題思路
算出一個整數的二進制中有幾個 1 位元,直覺反應用用右移(">>")和位元 AND ('&') 配合迴圈檢查每個位元,時間複雜度 O(n)
int popCount(int n) { int res = 0; while(n) { res += n & 1; n >>= 1; } return res; }
|
但這樣子還不夠快,所以改用建表 + 查表 O(1) 的方法。
作法很簡單,把每兩位元當作一組那每組就有
00, 01, 10, 11
四種狀態,1 出現的次數分別是
0 1 1 2
而當有兩組時變成
0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111,
1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111
4 * 4 = 16 種狀態,而 1 出現的次數剛好能將他拆成左邊一組右邊一組,然後左右兩組相加,變成
(0 + 0), (0 + 1), (0 + 1), (0 + 2), (1 + 0), (1 + 1), (1 + 1), (1 + 2),
(1 + 0), (1 + 1), (1 + 1), (1 + 2), (2 + 0), (2 + 1), (2 + 1), (2 + 2)
之後 6 位, 8 位 … 32 位皆可用同理求得,而由於過程是簡單遞迴,所以可以用巨集來建表減少程式的運算消耗 (如下方程式碼),最後查表輸出,但由於 232 太大了,所以表只建到 216 然後用右移(">>")和位元 AND ('&') 來拆成前 16 位和後 16 位再相加求得 1 的總數輸出。
輸入輸出量極大,要做 IO 優化。
–
題外話: 本題出題者應該是想要用時間卡開頭 O(n) 的算法,但本來 O(n) 就沒多慢,所以連帶著用 O(1) 算法但 IO 優化不夠好的人一起被卡掉了,結果就產生了討論區精彩的筆戰,然後不知道是不是因為太精采的關係,每次 Loading 進這題都要讀很久…
完整程式碼
AC (0.2s, 2.1MB)
#pragma GCC optimize(3) #include <stdio.h> #define MAX 65535 #define BUFSIZ 1048576 #define BUFMAX 1048560 #define BIT2(n) n, n+1, n+1, n+2 #define BIT4(n) BIT2(n), BIT2(n+1), BIT2(n+1), BIT2(n+2) #define BIT6(n) BIT4(n), BIT4(n+1), BIT4(n+1), BIT4(n+2) #define BIT8(n) BIT6(n), BIT6(n+1), BIT6(n+1), BIT6(n+2) #define BIT10(n) BIT8(n), BIT8(n+1), BIT8(n+1), BIT8(n+2) #define BIT12(n) BIT10(n), BIT10(n+1), BIT10(n+1), BIT10(n+2) #define BIT14(n) BIT12(n), BIT12(n+1), BIT12(n+1), BIT12(n+2) #define BIT16(n) BIT14(n), BIT14(n+1), BIT14(n+1), BIT14(n+2)
const unsigned char TABLE[MAX + 1] = { BIT16(0) }; char output[BUFSIZ];
inline char readChar() { static char buffer[BUFSIZ], * now = buffer + BUFSIZ, * end = buffer + BUFSIZ; if (now == end) { if (end < buffer + BUFSIZ) return EOF; end = (buffer + fread(buffer, 1, BUFSIZ, stdin)); now = buffer; } return *now++; }
inline char readUInt(unsigned int* dst) { register char ch; while ((ch = readChar()) < '0') if (ch == EOF) return 0; *dst = ch ^ '0'; while ((ch = readChar()) >= '0') * dst = (*dst << 3) + (*dst << 1) + (ch ^ '0'); return 1; }
char* setUInt(char buffer[], register unsigned int src, const char suffix) { register unsigned int div; char tmp[11], * st = tmp + 10, *res = buffer - 1; *st = 0; do { *(--st) = src - ((div = src / 10) << 3) - (div << 1) + '0'; } while (src = div); while (*++res = *st++) ; if (suffix)* res++ = suffix; return res; }
int main() { int n; char *end = output + BUFMAX; register char* oTop = output; while (readUInt(&n)) { oTop = setUInt(oTop, TABLE[n & MAX] + TABLE[(n >> 16) & MAX], '\n'); if (oTop > end) { *oTop = 0; fputs(output, stdout); oTop = output; } } *oTop = 0; fputs(output, stdout); return 0; }
|