京東2018校招 神奇數 (c/c++)
阿新 • • 發佈:2019-01-29
廢話
上週末被學長遠端抓壯丁答狗東2018的C++筆試題。2個小時4道大題,一個人做確實時間緊,記錄一下學長甩給我第二題的神奇數。
想來自從6月份畢業就沒再做過題,手生寫的慢,好賴算是ac了,後來聽說這題現場ac率10%我也是挺吃鯨的 =、=
閒話不多說,看題。
神奇數(京東2018校招C/C++工程師筆試大題第二道)
時間限制:1秒
空間限制:32768K
題目描述:
東東在一本古籍上看到有一種神奇數,如果能夠將一個數的數字分成兩組,其中一組數字的和等於另一組數字的和,我們就將這個數成為神奇數。例如242就是一個神奇數,我們能夠將這個數的數字分成兩組,分別是{2, 2}以及{4},而且這兩組數的和都是4。東東現在需要統計給定區間內中有多少個神奇數,即給定區間(l, r),統計這個區間中有多少個神奇數,請你來幫助他。
分析及解法
先把題目對神奇數的描述翻譯成人話:“給你一個數,按位拆開後分成兩堆兒,兩堆兒的和相等”。
我們把區間內的數字按位拆開可以得到一個n位的陣列,然後求其組合:
對組合結果求和,判斷是否等於按位總和的一半。
Code
#include <cstdio>
#include <vector>
#ifndef NO_WIN32_TIMER
#include <Windows.h>
#endif
using std::vector;
static unsigned long g_target; //按位總和的一半
static bool g_flag; //是否是神奇數
static vector<char> g_fenjie; //按位分解結果
static vector<char> g_zuhe; //組合結果
// 對待分析的數字進行拆解,結果存入g_fenjie
// 如果按位和為奇數則一定不是神奇數,直接返回false
bool GetSumAndAnalyze(unsigned int num) {
int fenjie_qiuhe = 0;
g_fenjie.clear();
do {
fenjie_qiuhe += num % 10 ;
g_fenjie.push_back(num % 10);
} while (num /= 10);
if (0 == fenjie_qiuhe % 2) {
g_target = fenjie_qiuhe / 2;
return true;
}
return false;
}
// 遞迴求組合,傳入當前遍歷到的陣列下標和剩餘個數
void Combination(int index, int number) {
if (g_flag) return;
if (!number) {
int sum = 0;
for (auto iter : g_zuhe)
sum += iter;
if (sum == g_target)
g_flag = true;
return;
}
if (index == g_fenjie.size()) return;
g_zuhe.push_back(g_fenjie[index]);
Combination(index + 1, number - 1);
g_zuhe.pop_back();
Combination(index + 1, number);
}
// 遞迴C(1/n) ~ C((n/2)/n),求到n-1就可以覆蓋全部組合
void Combination()
{
int length = g_fenjie.size();
g_zuhe.clear();
for (int i = 1; i <= length / 2; ++i) {
Combination(0, i);
if (g_flag) break;
}
}
// RT
bool IsMegicNumber(unsigned int num) {
if (!GetSumAndAnalyze(num))
return false;
g_flag = false;
Combination();
return g_flag;
}
// 呼叫IsMegicNumber求區間(l, r)內神奇數的個數.
int MegicNumberCnt(unsigned int l, unsigned int r) {
g_fenjie.reserve(10);
g_zuhe.reserve(10);
int cnt = 0;
for (unsigned int i = l; i <= r; ++i)
if (IsMegicNumber(i)) ++cnt;
return cnt;
}
// 測試並計時
int main()
{
#ifndef NO_WIN32_TIMER
LARGE_INTEGER t1, t2, tc;
QueryPerformanceFrequency(&tc);
double cost;
QueryPerformanceCounter(&t1);
#endif
printf("%d ", MegicNumberCnt(1, 65535));
#ifndef NO_WIN32_TIMER
QueryPerformanceCounter(&t2);
cost = (double)(t2.QuadPart - t1.QuadPart)*1.0 / tc.QuadPart;
printf("cost time: %f s.\n", cost);
#endif
getchar();
return 0;
}
繼續廢話
網上看見一篇核心思路用01揹包做的,直接上鍊接:
核心思想其實和組合差不多,都是搓堆兒湊一半,但可能有一半冗餘計算。
效率不如直接用組合,簡單做了個1~65535區間內對照測試驗證: