1. 程式人生 > >Google APAC 2016 University Graduates Test Round A解題報告

Google APAC 2016 University Graduates Test Round A解題報告

測試結果

注意到K最大是1018,每一個字串的字首都是前一個字串,因此只要找到第一個長度不小於K的Sn,然後遞迴求解就行了。由於字串長度是指數增長的,因此遞迴層數很小。

GG = 10**19
sz = [0]
while sz[-1] < GG:
    sz.append(sz[-1] * 2 + 1)
R = lambda: int(raw_input().strip())
T = R()
def solve(pos, kk):
    assert(sz[pos] >= kk)
    mid = sz[pos] / 2 + 1
    if kk == mid: return
0 elif kk < mid: return solve(pos - 1, kk) else: kk -= mid return 1 - solve(pos - 1, sz[pos - 1] - kk + 1) for i in xrange(T): print 'Case #' + str(i + 1) + ': ', K = R() print solve(len(sz) - 1, K)

B.gCube

將各個維度相乘就可以算出體積,對於目標長度,可以通過二分得到答案。我一開始沒注意到大資料會有乘法溢位的問題,導致大資料掛了,要不然就滿分了。。。解決浮點乘法溢位的辦法也很簡單:取對數。把乘法變成加法。

#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> point;
void solve() {
    int N, Q, l, r;
    cin >> N >> Q;
    vector<double> arr(N);
    FOR(i, N) cin >> arr[i];
    FOR(i, Q) {
        cin
>> l >> r; long double cur = 0.0; for (int j = l; j <= r; ++j) { cur += log(arr[j]); } long double low = 0.0, up = 1e10; int cnt = 0; while (cnt < 1000) { ++cnt; long double mid = (low + up) / 2.0; long double val = (long double)(r - l + 1) * log(mid); if (val > cur) up = mid; else low = mid; } cout << fixed << setprecision(10) << (low + up) / 2.0 << endl; } return; } int main() { int TestCase; cin >> TestCase; FOR(caseID, TestCase) { cout << "Case #" << caseID + 1 << ":" << endl; solve(); } return 0; }

簡單來說,就是要求判斷一條邊是否有可能出現在最短路上,一個比較簡單的圖論題。首先通過floyd演算法求出所有點對之間的最短路徑,時間複雜度O(N3),然後對於每一條邊(u,v),列舉所有的頂點對,判斷是否有下式成立:

distance(i, j) = distance(i, u) + w(u, v) + distance(v, j)

如果上式對於某個(i,j)頂點對成立,則說明(u,v)這條邊在某條最短路徑上。

#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> point;
const ll INF = (ll)1e14;
struct Edge {
    int from, to, idx;
    ll wei;
    Edge(int _f, int _t, ll _w, int _i): from(_f), to(_t), wei(_w), idx(_i) {}
};
void solve() {
    int n, m, x, y;
    ll w;
    scanf("%d %d", &n, &m);
    vector<Edge> arr;
    ll g[105][105];
    FOR(i, n) FOR(j, n) {
        if (i == j) g[i][j] = 0;
        else g[i][j] = INF;
    }
    FOR(i, m) {
        cin >> x >> y >> w;
        g[x][y] = min(g[x][y], w);
        g[y][x] = min(g[y][x], w);
        arr.push_back(Edge(x, y, w, i));
    }
    ll dis[105][105];
    FOR(i, n) FOR(j, n) {
        dis[i][j] = g[i][j];
    }
    FOR(k, n) FOR(i, n) FOR(j, n) {
        dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
    }
    vector<bool> ok(m, false);
    FOR(i, m) {
        int f = arr[i].from, t = arr[i].to;
        ll w = arr[i].wei;
        FOR(j, n) FOR(k, n) {
            if (ok[i] || dis[j][k] == dis[j][f] + w + dis[t][k]) {
                ok[i] = true;
                break;
            }
        }
        if (!ok[i]) {
            cout << i << endl;
        }
    }
    return;
}
int main() {
    int TestCase;
    cin >> TestCase;
    FOR(caseID, TestCase) {
        cout << "Case #" << caseID + 1 << ":" << endl;
        solve();
    }
    return 0;
}

一個看上去比較麻煩的模擬題,google每年都會出這種題目,關鍵在於要把思路理清楚,讓程式碼儘量簡潔一點,否則很容易出bug。

對於貪吃蛇的身體,由於每次只是頭部和尾部可能發生變化,因此只需要維護一個雙端佇列deque,每次向前移動,刪除尾部元素,更新頭部元素即可。

對於吃食物,可以用一個集合set來維護那些食物已經被吃過了,雖然總的食物點可能達到1010,但注意到Xi不超過106,而一旦沒有了命令,貪吃蛇只能在一個方向前進,最多額外吃105單位食物,因此記憶體是完全可以承受的。

對於判斷貪吃蛇是否因為碰到自己身體而死的情況,可以用雜湊表map來維護每個位置上的貪吃蛇身體數目,一旦發現一個位置上出現了超過一個貪吃蛇身體,就說明它碰到了自己的身體,遊戲結束。

最終程式碼只有80行。

#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> point;
typedef pair<int, char> rec;
const int dir_x[] = {1, 0, -1, 0};
const int dir_y[] = {0, -1, 0, 1};
set<point> eat;
inline bool has_food(point& pos) {
    int cc = pos.first + pos.second;
    if (cc % 2 == 0) return false;
    if (eat.find(pos) != eat.end()) return false;
    return true;
}
inline void eat_food(point& pos) {
    eat.insert(pos);
}
void solve() {
    int Q, row, col;
    cin >> Q >> row >> col;
    deque<point> snake;
    eat.clear();
    int d = 3, ptr = 0;
    vector<rec> cmd(Q);
    FOR(i, Q) cin >> cmd[i].first >> cmd[i].second;
    sort(cmd.begin(), cmd.end());
    map<point, int> visited;
    snake.push_back({0, 0});
    visited[{0, 0}] = 1;
    int tt = 1;
    while (true) {
        if (tt > 1500000) {
            cout << snake.size() << endl;
            return;
        }
        point tp = snake.front();
        tp.first = (tp.first + dir_x[d] + row) % row;
        tp.second = (tp.second + dir_y[d] + col) % col;
        if (!has_food(tp)) {
            --visited[snake.back()];
            if (visited[snake.back()] == 0) visited.erase(snake.back());
            ++visited[tp];
            if (visited[tp] >= 2) {
                cout << snake.size() << endl;
                return;
            }
            snake.push_front(tp);
            snake.pop_back();
        }
        else {
            eat_food(tp);
            ++visited[tp];
            if (visited[tp] >= 2) {
                cout << snake.size() << endl;
                return;
            }
            snake.push_front(tp);
        }
        if (ptr < cmd.size() && cmd[ptr].first == tt) {
            if (cmd[ptr].second == 'L') d = (d - 1 + 4) % 4;
            else {
                d = (d + 1) % 4;
                assert(cmd[ptr].second == 'R');
            }
            ++ptr;
        }
        ++tt; // increase clock
    }
    return;
}
int main() {
    int TestCase;
    cin >> TestCase;
    FOR(caseID, TestCase) {
        cout << "Case #" << caseID + 1 << ": ";
        solve();
    }
    return 0;
}

第一次用markdown在csdn上寫部落格,請大家多指教。

也祝2016年畢業的童鞋拿到理想的offer,我參加這個筆試完全是for fun,對於被我擠掉名額的那個同學說一聲sorry。。。