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

照例先传个成绩截图:这里写图片描述
比赛地址:https://code.google.com/codejam/contest/4284487/dashboard,仍然可以练习。

A.gRanks

这个比较水,计算一下每个人的总分,排一下序就行了。

#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
inline void solve() {
    int P, N, M;
    scanf("%d", &P);
    vector<int> score(P);
    FOR(i, P) scanf("%d", &score[i]);
    scanf("%d", &N);
    vector<int> w(N);
    map<string, vector<int> > mp;
    string name;
    FOR(i, N) {
        scanf("%d", &w[i]);
        FOR(j, P) {
            cin >> name;
            mp[name].push_back(score[j] * w[i]);
        }
    }
    scanf("%d", &M);
    vector<pair<int, string> > arr;
    for (auto it = mp.begin(); it != mp.end(); ++it) {
        vector<int>& v = it->second;
        sort(v.begin(), v.end(), greater<int>());
        if ((int)v.size() >= M) arr.push_back({accumulate(v.begin(), v.begin() + M, 0), it->first});
        else arr.push_back({accumulate(v.begin(), v.end(), 0), it->first});
    }
    sort(arr.begin(), arr.end(), greater<pair<int, string> >());
    int ptr = 0;
    while (ptr < (int)arr.size()) {
        int i = ptr;
        while (i < (int)arr.size() && arr[i].first == arr[ptr].first) ++i;
        reverse(arr.begin() + ptr, arr.begin() + i);
        for (int j = ptr; j < i; ++j) {
            printf("%d: %s\n", ptr + 1, arr[j].second.c_str());
        }
        ptr = i;
    }
}
int main() {
    int T;
    scanf("%d", &T);
    FOR(caseID, T) {
        cout << "Case #" << caseID + 1 << ":" << endl;
        solve();
    }
    return 0;
}

B.gFiles

每一条信息都表达了一个文件总数上界和下界的不等式:

Pi/100Ki/N<(Pi+1)/100

然后每次更新上界和下界,到最后,如果上界和下界相等,则说明解唯一,否则不唯一。

需要注意, P=100 是一个特殊情况,需要单独处理。

#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void solve() {
    int N;
    scanf("%d", &N);
    ll mn = 0, mx = ~(1ll << 63);
    vector<ll> P(N), K(N);
    bool ok = false;
    FOR(i, N) {
        cin >> P[i] >> K[i];
        if (ok) continue;
        if (P[i] == 100 && !ok) {
            ok = true;
            mn = mx = K[i];
            continue;
        }
        K[i] = K[i] * 100;
        if (P[i] > 0) mx = min(mx, K[i] / P[i]);
        mn = max(mn, K[i] / (P[i] + 1) + 1ll);
    }
    if (mn == mx) printf("%lld\n", mn);
    else printf("-1\n");
    return;
}
int main() {
    int TestCase;
    cin >> TestCase;
    FOR(caseID, TestCase) {
        cout << "Case #" << caseID + 1 << ": ";
        solve();
    }
    return 0;
}

C.gGames

首先注意到,按照[ 0 , 2N1]编号,如果 i j第1轮不能相遇,那么 i j去掉二进制最低1位不能相等,同理,如果在第2轮不能相遇,那么去掉二进制最低2位不能相等。

对于小数据, 2N8 ,暴力枚举每一个排列验证就行了。

对于大数据,枚举所有排列会超时,正解是动态规划。。经过剪枝优化的搜索也是可以混过去的。。。

#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
int g[20][20], total, prob[20][20];
vector<int> perm;
bool ok, visited[20];
void dfs(int pos) {
    if (ok || pos == total) {
        ok = true;
        return;
    }
    for (int i = 1; i < total; ++i) {
        if (visited[i] || prob[pos][i] > 0) continue;
        if (i % 2 && !visited[i - 1]) continue;
        visited[i] = true;
        perm[pos] = i;
        for (int j = pos + 1; j < total; ++j) {
            if (g[pos][j] > 0) {
                int cur = i - i % (1 << g[pos][j]);
                FOR(k, 1 << g[pos][j]) ++prob[j][k + cur];
            }
        }
        dfs(pos + 1);
        if (ok) return;
        for (int j = pos + 1; j < total; ++j) {
            if (g[pos][j] > 0) {
                int cur = i - i % (1 << g[pos][j]);
                FOR(k, 1 << g[pos][j]) --prob[j][k + cur];
            }
        }
        visited[i] = false;
    }
}
void solve() {
    int N, M, E, K, B, X;
    cin >> N >> M;
    memset(g, 0, sizeof g);
    int mx = 0;
    FOR(i, M) {
        cin >> E >> K >> B;
        mx += (K >= N);
        --E;
        FOR(j, B) {
            cin >> X; --X;
            g[E][X] = max(K, g[E][X]);
            g[X][E] = max(K, g[X][E]);
        }
    }
    if (mx > 0) { printf("NO\n"); return; }
    total = (1 << N);
    perm.resize(total);
    memset(visited, false, sizeof visited);
    memset(prob, 0, sizeof prob);
    perm[0] = 0; visited[0] = true;
    FOR(i, total) {
        if (g[0][i] > 0) {
            for (int j = 0; j < (1 << g[0][i]); ++j) {
                ++prob[i][j];
            }
        }
    }
    ok = false; dfs(1);
    if (ok) printf("YES\n");
    else printf("NO\n");
    return;
}
int main() {
    int TestCase;
    cin >> TestCase;
    FOR(caseID, TestCase) {
        cout << "Case #" << caseID + 1 << ": ";
        solve();
    }
    return 0;
}

上述搜索 + 剪枝的代码在大数据上跑了21秒,远小于8分钟,还可以承受。。

动态规划的状态是当前还剩下哪些位置,每次检验当前是否有冲突,没有冲突的话再对半分,递归检验,用备忘录记录一下中间结果。

#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
int g[20][20], total;
bool visited[20];
int dp[1 << 16];
vector<int> arr[16];
int dfs(int status, int cnt) {
    if (dp[status] >= 0) return dp[status];
    if (cnt == 0) {
        dp[status] = 1;
        return dp[status];
    }
    FOR(i, total) {
        if (((1 << i) & status) == 0) continue;
        for (int j = i + 1; j < total; ++j) {
            if (g[i][j] >= cnt) {
                int a = ((1 << j) & status);
                if (a > 0) { dp[status] = 0; return dp[status]; }
            }
        }
    }
    int index = (1 << (cnt - 1));
    FOR(i, arr[index].size()) {
        int a = arr[index][i];
        int b = status - a;
        if ((status & a) == a && a < b) {
            dp[status] = dfs(a, cnt - 1) && dfs(b, cnt - 1);
        }
        if (dp[status] > 0) return dp[status];
    }
    return dp[status];
}
void solve() {
    int N, M, E, K, B, X;
    cin >> N >> M;
    memset(g, 0, sizeof g);
    FOR(i, M) {
        cin >> E >> K >> B;
        --E;
        FOR(j, B) {
            cin >> X; --X;
            g[E][X] = max(K, g[E][X]);
            g[X][E] = max(K, g[X][E]);
        }
    }
    total = (1 << N);
    FOR(i, 1 << total) dp[i] = -1;
    if (dfs((1 << total) - 1, N)) printf("YES\n");
    else printf("NO\n");
}
int main() {
    FOR(i, 1 << 16) {
        arr[__builtin_popcount(i)].push_back(i);
    }
    int TestCase;
    cin >> TestCase;
    FOR(caseID, TestCase) {
        cout << "Case #" << caseID + 1 << ": ";
        solve();
    }
    return 0;
}

上述代码在大数据上只跑了0.1秒。

D.gMatrix

这道题目其实相当简单,有一个经典的问题是:给定一个长为 N 的序列,要求O(N)时间求出所有长为 K 的连续子序列中的最大值。用双端队列维护一个单调下降的deque就可以了。这个问题只不过是扩展到了二维,首先对每一行求一遍最大值,然后再对每一列求一遍最大值就行了,时间复杂度O(N2)

#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<ll, int> pii;
void solve() {
    ll N, K, C, X;
    cin >> N >> K >> C >> X;
    vector<ll> A(N), B(N);
    FOR(i, N) cin >> A[i];
    FOR(i, N) cin >> B[i];
    vector<vector<ll> > arr(N, vector<ll>(N));
    FOR(i, N) FOR(j, N) {
        arr[i][j] = ((i + 1) * A[i] + (j + 1) * B[j] + C) % X;
    }
    deque<pii> dp;
    FOR(i, N) {
        dp.clear();
        FOR(j, N) {
            while (!dp.empty() && dp.front().second <= j - K) dp.pop_front();
            while (!dp.empty() && (dp.back().second <= j - K || arr[i][j] >= dp.back().first)) dp.pop_back();
            dp.push_back(make_pair(arr[i][j], j));
            arr[i][j] = dp.front().first;
        }
    }
    ll res = 0;
    for (int j = K - 1; j < N; ++j) {
        dp.clear();
        FOR(i, N) {
            while (!dp.empty() && dp.front().second <= i - K) dp.pop_front();
            while (!dp.empty() && (dp.back().second <= i - K || arr[i][j] >= dp.back().first)) dp.pop_back();
            dp.push_back(make_pair(arr[i][j], i));
            arr[i][j] = dp.front().first;
            if (i >= K - 1) res += arr[i][j];
        }
    }
    printf("%lld\n", res);
    return;
}
int main() {
    int TestCase;
    cin >> TestCase;
    FOR(caseID, TestCase) {
        cout << "Case #" << caseID + 1 << ": ";
        solve();
    }
    return 0;
}

以上。。Round C比Round B要简单一些。。。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值