Google APAC 2016 University Graduates Test Round A解题报告

测试结果

这是google 2016年校招的在线笔试第一轮,题目的链接可点击https://code.google.com/codejam/contest/4284486/dashboard,由于大多是考察基础知识,所以比竞赛题目要简单很多。

A.Googol String

注意到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;
}

C. gCampus

简单来说,就是要求判断一条边是否有可能出现在最短路上,一个比较简单的图论题。首先通过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;
}

D.gSnake

一个看上去比较麻烦的模拟题,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。。。

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值