滴滴出行2017秋招筆試真題-程式設計題彙總
滴滴的題考經典演算法比較多啊,兩道經典動態規劃,一道經典搜尋題,一道程式設計之美原題(聽別人說是程式設計之美上的,自己並不清楚),兩道水題.
題目連結:[點這兒].
第一題:
題目:連續最大和
求陣列的連續最大和,太經典了,有
dp
的做法,也有非dp
的線性做法,我用的dp
.
程式碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
int n;
while (cin >> n) {
vector <int> arr;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
arr.push_back(x);
}
vector<LL> dp(n, 0);
LL ans = arr[0];
dp[0] = arr[0];
for (int i = 1; i < n; i++) {
dp[i] = dp[i - 1] < 0 ? arr[i] : dp[i - 1] + arr[i];
ans = max(dp[i], ans);
}
cout << ans << endl;
}
return 0;
}
第二題:餐館
題目:
某餐館有
n
張桌子,每張桌子有一個引數:a
可容納的最大人數; 有m
批客人,每批客人有兩個引數:b
人數,c
預計消費金額。 在不允許拼桌的情況下,請實現一個演算法選擇其中一部分客人,使得總預計消費金額最大
解析:
貪心,很容易想到,消費高並且人數少的客人們是要優先考慮的(店家都喜歡做土豪的生意嘛).其次要把這些客人安排到大小恰到合適的桌子,這樣可以接納更多的客人(避免浪費,店家也最喜歡做這種事).
這樣就可以直接寫出一個複雜度為
O(n2) 的演算法,但是這個題的資料用這樣的演算法是過不了的,必須優化到O(nlogn) 。我們可以想到在
O(n2) 做法中有個迴圈是找桌子,順序查詢的,這個時候桌子的容量已經排好序了,那麼很容易就可以從這兩個條件看出來可以二分查詢來優化,於是我用了stl
中的multiset.lower_bound
來實現這個二分.後來在評論區看到有人用優先佇列做的,其實是一樣的,優先佇列的查詢最大值也是
O(logn) 的,這種做法只不過是O(logn∗n) 也就是說和我的做法只是迴圈內外層調換了下,有興趣的可以試試.
程式碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
bool cmp(const pair<int, int> &A, const pair<int, int> &B)
{
if (A.second != B.second)
return A.second > B.second;
return A.first < B.first;
}
int main()
{
int n, m;
while (cin >> n >> m) {
multiset<int> a;
vector<pair<int, int> > arr;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
a.insert(x);
}
for (int i = 0; i < m; i++) {
int x, y;
cin >> x >> y;
arr.emplace_back(x, y);
}
sort(arr.begin(), arr.end(), cmp);
LL ans = 0;
for (int i = 0; i < m && a.size() > 0; i++) {
auto it = a.lower_bound(arr[i].first);
if (it != a.end()) {
ans += arr[i].second;
a.erase(it);
}
}
cout << ans << endl;
}
return 0;
}
第三題:
題目:地下迷宮
小青蛙有一天不小心落入了一個地下迷宮,小青蛙希望用自己僅剩的體力值
P
跳出這個地下迷宮。為了讓問題簡單,假設這是一個n*m
的格子迷宮,迷宮每個位置為0
或者1
,0
代表這個位置有障礙物,小青蛙達到不了這個位置;1
代表小青蛙可以達到的位置。小青蛙初始在(0,0)
位置,地下迷宮的出口在(0,m-1)
(保證這兩個位置都是1,並且保證一定有起點到終點可達的路徑),小青蛙在迷宮中水平移動一個單位距離需要消耗1
點體力值,向上爬一個單位距離需要消耗3
個單位的體力值,向下移動不消耗體力值,當小青蛙的體力值等於0的時候還沒有到達出口,小青蛙將無法逃離迷宮。現在需要你幫助小青蛙計算出能否用僅剩的體力值跳出迷宮(即達到(0,m-1)
位置)。
解析:
bfs
,已經寫過太多這種題了,不想寫這個題解析了,可以看看我部落格的其他文章,裡面有這類題的解析.
程式碼:
#include <bits/stdc++.h>
using namespace std;
int dirx[] = {-1, 0, 1, 0};
int diry[] = {0, 1, 0, -1};
int value[] = {3, 1, 0, 1};
struct Node {
int x, y;
int p, pre;
Node(int x, int y, int p, int pre = -1) : x(x), y(y), p(p), pre(pre) {}
bool operator < (const Node &other) const {
return p > other.p;
}
};
int main()
{
int n, m, p;
while (cin >> n >> m >> p) {
vector<vector<int> > mp(n, vector<int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int x;
cin >> x;
mp[i][j] = x;
}
}
deque<Node> que;
vector<vector<bool> > used(n, vector<bool>(m, false));
que.push_back(Node(0, 0, p));
int head = 0;
used[0][0] = true;
bool f = false;
int ans = 0;
while (!que.empty()) {
if (que.begin() + head == que.end())
break;
auto now = que[head++];
if (now.x == 0 && now.y == m - 1) {
f = true;
ans = head - 1;
break;
}
for (int i = 0; i < 4; i++) {
int tx = now.x + dirx[i], ty = now.y + diry[i];
int tp = now.p - value[i];
if (tx >= 0 && tx < n && ty >= 0 && ty < m && mp[tx][ty] == 1 && !used[tx][ty] && tp >= 0)
que.push_back(Node(tx, ty, tp, head - 1)), used[tx][ty] = true;
}
}
if (!f) {
cout << "Can not escape!" << endl;
} else {
stack<int> st;
while (que[ans].pre != -1)
st.push(que[ans].pre), ans = que[ans].pre;
while (!st.empty())
cout << "[" << que[st.top()].x << "," << que[st.top()].y << "],", st.pop();
cout << "[" << 0 << "," << m - 1 << "]" << endl;
}
}
return 0;
}
第四題:末尾0的個數
題目:
輸入一個正整數
n
,求n!
(即階乘)末尾有多少個0? 比如:n = 10; n! = 3628800
,所以答案為2
.
解析:
n!=1×2×⋯×k×⋯×n 。首先分析下什麼時候末尾才會出現0,當k 中有5這個因子的時候與偶數相乘就會出現0,因此,這個題轉化為n! 中有多少個5因子;n!=1×2×⋯×(5∗1)×⋯×9×(5∗2)×⋯24×(5∗5∗1)×⋯(5∗5∗5)×⋯
可以發現有一個5的因子的數是每隔5個就有,有兩個5的因子的數是每隔25個就有,……,那麼很直接的做法就是下面程式碼的做法了(每隔5個數有1個5,然後再是每隔25個數再有1個5,累加起來就好了).
程式碼:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
while (cin >> n) {
int ans = 0;
for (; n; ans += n /= 5);
cout << ans << endl;
}
return 0;
}
第五題:進位制轉換
題目:
給定一個十進位制數
M
,以及需要轉換的進位制數N
。將十進位制數M
轉化為N
進位制數,M
(32位整數)、N(2 ≤ N ≤ 16)
.
解析:
水題,直接模擬,我的程式碼應該是最簡潔的吧.
#include <bits/stdc++.h>
using namespace std;
const char MP[] = "0123456789ABCDEF";
int main()
{
int M, N;
while (cin >> M >> N) {
stack<int> st;
bool f = false;
for (M = M < 0 ? f = true, -M : M; M; st.push(M % N), M /= N);
for (f ? cout << "-", 0 : 0; !st.empty(); cout << MP[st.top()], st.pop());
cout << endl;
}
return 0;
}
第六題:數字和為sum的方法數
題目:
給定一個有n個正整數的陣列A和一個整數sum,求選擇陣列A中部分數字和為sum的方案數。
當兩種選取方案有一個數字的下標不一樣,我們就認為是不同的組成方案。
解析:
最經典的01揹包問題,直接寫就好了,如果這個題沒有這句話”當兩種選取方案有一個數字的下標不一樣,我們就認為是不同的組成方案。 “,那麼這就是個多重揹包問題了.
程式碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main()
{
int n, sum;
while (cin >> n >> sum) {
vector<int> arr(n);
for (int i = 0; i < n; i++)
cin >> arr[i];
vector<LL> dp(sum + 1, 0);
dp[0] = 1;
for (int i = 0; i < n; i++)
for (int j = sum; j >= arr[i]; j--)
dp[j] += dp[j - arr[i]];
cout << dp[sum] << endl;
}
return 0;
}