Goolge Kick Start Round A 2020 (A ~ D题题解)

比赛链接:kick start Round A 2020

A. Allocation

题目链接

题意

给出 \(N\) 栋房子的价格,第 \(i\) 栋房子的价格为 \(A_i\),你有 \(B\) 美元,问最多可以买多少栋房子?

思路

典型的贪心问题,将所有的房子按价格从低到高排序后选取即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int _, Case;
void solve() {
    Case++;
    int n, b;
    cin >> n >> b;
    int a[n];
    for (int i = 0; i < n; ++i) cin >> a[i];
    sort(a, a + n);
    int ans = 0;
    for (int i = 0; i < n; ++i) {
        if (b < a[i])
            break;
        else
            b -= a[i], ans++;
    }
    cout << "Case #" << Case << ": " << ans << endl;
}
int main() {
    // freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> _;
    while (_--) solve();
}

B. Plates

题目链接

题意

\(N\) 叠盘子,每叠有 \(K\) 个盘子,每个盘子有个 beauty value,现在要拿 \(P\) 个盘子,使得 beauty value 之和最大。拿盘子的条件:如果一个盘子的上面的盘子都被拿走了,才能拿到这个盘子。

思路

本题有一点点像多重背包。首先计算每叠盘子各自的前缀和 \(sum[N][K]\),设 \(dp[i][j]\) 表示前 \(i\) 堆盘子中取 \(j\) 个盘子的 beauty value 的最大值,转移方程为:

\[dp[i][j]=max\{dp[i][j],dp[i−1][j−l]+sum[i][l]|\ for\ l ∈\ [0,min(j,k)]\} \]

代码

#include <bits/stdc++.h>
#define ms(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int N = 1510;
int _, Case = 0;
int n, p, k, x;
int sum[N][N], a[N][N], dp[N][N];
void solve() {
    ms(sum, 0), ms(a, 0), ms(dp, 0);//初始化
    cin >> n >> k >> p;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= k; ++j) cin >> a[i][j];
    cout << "Case #" << ++Case << ": ";
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= k; ++j) sum[i][j] = sum[i][j - 1] + a[i][j];
    for (int i = 0; i <= k; ++i) dp[1][i] = sum[1][i];
    for (int i = 2; i <= n; ++i)
        for (int j = 1; j <= p; ++j)  //这里不需要太大,到p就够了
            for (int l = 0; l <= min(j, k); ++l)//接下来就是写状态转移方程了
                dp[i][j] = max(dp[i][j], dp[i - 1][j - l] + sum[i][l]);

    cout << dp[n][p] << endl;
}
int main() {
    // freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    for (cin >> _; _; _--) solve();
}

C. Workout

题目链接

题意

给定 \(N\) 个严格递增的数,往这 \(N\) 个数中间插 \(K\) 个数,插入后要使所有的数仍然保持严格递增,而且要保证相邻两数的最大绝对值之差最小,求最小的绝对值之差。

思路

想了挺久的,最后二分过的,典型的最大值最小的问题。判断函数的思路:传入参数 \(x\),表示答案为 \(x\),然后遍历每个数 \(num[i]\),如果 \(num[i+1]−num[i]>x\),就插入 \(num[i]+x\),如果插入的数的个数大于 \(K\) 个就返回 false,否则返回 true。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;
int _, Case;
int n, k;
ll tmp[N], a[N];
bool check(int x) {
    int cnt = 0;
    for (int i = 0; i < n - 1; ++i)
        while (tmp[i + 1] - tmp[i] > x) tmp[i] += x, cnt++;
    if (cnt > k) return false;
    return true;
}
void solve() {
    cin >> n >> k;
    for (int i = 0; i < n; ++i) cin >> a[i];
    cout << "Case #" << ++Case << ": ";
    int l = 1, r = inf;
    while (l < r) {
        int mid = (l + r) >> 1;
        for (int i = 0; i < n; ++i) tmp[i] = a[i];
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    cout << r << endl;
}
int main() {
    // freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    for (cin >> _; _; _--) solve();
}

D. Bundling

题目链接

题意

给定 \(N\) 个字符串,把它们分组,每组 \(K\) 个。每组的分数是该组所有字符串的最长公共前缀。求最大的所有组的分数和。

思路

很容易想到前缀树 (字典树 / trie 树,相关算法讲解),建完树后从根节点 dfs,同时记录深度 d,然后从叶子节点回溯,统计每个节点出现的个数 cnt,如果某个节点 \(u\)\(cnt[u]≥K\),那么说明有 K 个字符串的前缀是以该节点结尾,深度 d 表示它们的前缀的长度,由于是从叶子节点回溯的,所以一定是最长公共前缀,所以 \(ans=ans+d\),同时 \(cnt[u]\) 减去 \(k\),即这 \(k\) 个字符串已经分完组,不再分到其他组。

代码

#include <bits/stdc++.h>
#define ms(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;

int trie[N << 2][30], tot = 1;
int cnt[N << 2], n, k, _, Case;
ll ans;
string s;

void init() { ms(trie, 0), ms(cnt, 0), tot = 1, ans = 0; }
void insert(string s) {
    int p = 0;
    for (auto c : s) {
        int idx = c - 'A';
        if (!trie[p][idx]) trie[p][idx] = tot++;
        p = trie[p][idx];
    }
    ++cnt[p];
}
void dfs(int u, int d) {  //起点,深度
    for (int v = 0; v < 26; ++v) {
        if (trie[u][v]) {
            dfs(trie[u][v], d + 1);
            cnt[u] += cnt[trie[u][v]];
        }
    }
    ans += cnt[u] / k * d, cnt[u] %= k;
}
void solve() {
    init();
    cout << "Case #" << ++Case << ": ";
    cin >> n >> k;
    for (int i = 0; i < n; ++i) cin >> s, insert(s);
    dfs(0, 0);
    cout << ans << endl;
}
int main() {
    // freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    for (cin >> _; _; _--) solve();
}

这是第一次做Google平台的比赛,感觉难易度适中(适合我这种蒟蒻,感觉以后可以多写写)

posted @ 2020-11-11 20:00  RioTian  阅读(196)  评论(0编辑  收藏  举报