牛客算法周周练1 【题解】

A、Maximize The Beautiful Value

戳我传送

解题思路:

如果你可以观察到给定的序列是递增的,这道题就是水题了,我们要求max,考虑到题目意思,那肯定是就移动K个位置,我们先求到图片说明 后面在预处理出前缀和。之和我们就可以从第K+1个位置(下标从1开始),依次向后枚举,计算F(n)的最大值。向前移动K个位置,就表明前面K项和全部多算一遍,第K+1个位置少算K遍。

时间复杂度 O(N)

Code

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll;

inline int read() {
    int s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

const int N = 1e5 + 7;

ll a[N];
ll sum[N];

int main() {
    int T = read();
    while (T--) {
        int n = read(), k = read();
        ll tot = 0;
        for (int i = 1; i <= n; ++i) {
            a[i] = read();
            tot += a[i] * i;
        }
        for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + a[i];
        ll ans = -(1 << 29);
        for (int i = k + 1; i <= n; ++i) {
            ll tmp = tot + (sum[i - 1] - sum[i - 1 - k]) - a[i] * k;
            ans = max(ans, tmp);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

B、身体训练

戳我传送

解题思路:

枚举每一个人(i),再枚举出发时间(j),那么时间总和就是路程除以相对速度图片说明 因为每个人概率都是1/n所以最后求和公式里面u*n可以抵消一个n

时间复杂度 O(N*N)

Code

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll;

inline int read() {
    int s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

const int N = 1e3 + 5;
double c[N], d[N];

int main() {
    int n;
    double v, u;
    scanf("%d %lf %lf", &n, &v, &u);
    for (int i = 0; i < n; ++i)  scanf("%lf", c + i);
    for (int i = 0; i < n; ++i)  scanf("%lf", d + i);
    double ans = 0.0;
    for (int i = 0; i < n; ++i)
        for (int j = 0; j < n; ++j)
            ans += u / (c[i] - j * d[i] - v);
    printf("%.3f\n", ans);
    return 0;
}

C、Borrow Classroom

戳我传送

解题思路

需要前导知识LCA:不懂的可以康康这位大佬写的博客:
https://blog.csdn.net/qq_43549984/article/details/100144030?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2

通过一次dfs求出以1为根的全部节点的深度,并且预处理出全部的fa数组。
1、同学A->教室 == deep[A]
2、同学B->同学C->教室 == deep[B]+2deep[C]-2deep[lca(b,c)] (如果觉得这个有点难理解的可以画图体会一下)
我们记式子1为ans1,式子2为ans2。
如果ans1<ans2一定可以拦截成功;
如果ans1==ans2;需要判断A、C是不是在同一棵子树上 即lca(a,c)是否等于1,如果等于1说明同时到达教室,手速跟不上,拦截失败,如果不是1说明在同一棵子树上,可以提前拦截下来。
如果ans1>ans2一定拦截失败。

时间复杂度 O(N * log(N)+q * log(N))

Code

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll;

inline int read() {
    int s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

const int N = 1e5 + 7;

vector<int> p[N];
int deep[N];
int fa[N][20]; //log2(100000)  = 16.

void dfs(int x, int y) { //dfs求到深度以及预处理x的倍增数组
    fa[x][0] = y;
    deep[x] = deep[y] + 1;
    for (int i = 1; i < 20; ++i)
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for (int i = 0; i < p[x].size(); ++i) {
        int tmp = p[x][i];
        if (tmp != y)
            dfs(tmp, x);
    }
}

int lca(int x, int y) {
    if (deep[x] < deep[y])    swap(x, y);
    for (int i = 19; i >= 0; --i)
        if (deep[fa[x][i]] >= deep[y])
            x = fa[x][i]; //调整到同一深度
    if (x == y)    return x;
    for (int i = 19; i >= 0; --i)
        if (fa[x][i] != fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

int main() {
    int T = read();
    while (T--) {
        for (int i = 0; i < N; ++i)  p[i].clear();
        int n = read(), m = read();
        for (int i = 1; i < n; ++i) {
            int x = read(), y = read();
            p[x].push_back(y);
            p[y].push_back(x);
        }
        dfs(1, 0);
        while (m--) {
            int a = read(), b = read(), c = read();
            int len1 = lca(b, c);
            int len2 = lca(a, c);
            int ans1 = deep[a];
            int ans2 = deep[b] + 2 * deep[c] - 2 * deep[len1];
            if (ans1 < ans2)    puts("YES");
            else {
                if (len2 != 1 && ans1 == ans2) puts("YES");
                else puts("NO");
            }
        }
    }
    return 0;
}

D、景区路线规划

戳我传送

解题思路

考查知识点记忆化搜索+dfs
通过dfs,依次从底向上处理,累加计算出子节点的平均期望,加上这个可去地点的h值,除以去过的地点数目,就是该节点的平均期望值。因为过程中涉及大量的重复时间计算,可以通过记忆化进行优化,因为涉及男女两个方面,需要写2个dfs,修改一下累加的期望值就ok了,也可以不用结构体变量保存,看个人。

时间复杂度 O(图片说明

Code

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll;

inline int read() {
    int s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

const int N = 105;
const double eps = 1e-6;
struct Node {
    int c, h1, h2;
}a[N];
int n, m, k, e[N][N];
double f1[N][500], f2[N][500]; //1男2女

double dfs1(int u, int t) {
    if (f1[u][t] > eps)    return f1[u][t];
    int cnt = 0; //去过的子节点数量
    double ans = 0.0;
    for (int i = 1; i <= n; ++i) {
        if (e[u][i] && t >= e[u][i] + a[i].c) { //存在足够的时间去到并且游玩完毕
            ++cnt;
            ans += dfs1(i, t - e[u][i] - a[i].c);
        }
    }
    if (cnt)    ans /= cnt;
    ans += a[u].h1;
    f1[u][t] = ans;
    return ans;
}

double dfs2(int u, int t) {
    if (f2[u][t] > eps)    return f2[u][t];
    int cnt = 0; //去过的子节点数量
    double ans = 0.0;
    for (int i = 1; i <= n; ++i) {
        if (e[u][i] && t >= e[u][i] + a[i].c) { //存在足够的时间去到并且游玩完毕
            ++cnt;
            ans += dfs2(i, t - e[u][i] - a[i].c);
        }
    }
    if (cnt)    ans /= cnt;
    ans += a[u].h2;
    f2[u][t] = ans;
    return ans;
}

int main() {
    n = read(), m = read(), k = read();
    for (int i = 1; i <= n; ++i)
        a[i].c = read(), a[i].h1 = read(), a[i].h2 = read();
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read(), t = read();
        e[u][v] = t;
        e[v][u] = t;
    }
    double ans1 = 0.0, ans2 = 0.0;
    for (int i = 1; i <= n; ++i) {
        if (k >= a[i].c) {
            ans1 += dfs1(i, k - a[i].c);
            ans2 += dfs2(i, k - a[i].c);
        }
    }
    ans1 /= n; ans2 /= n;
    printf("%.5f %.5f\n", ans1, ans2);
    return 0;
}

E、幸运数字Ⅱ

戳我传送

解题思路

可以通过观察在一段区间内的next值是相同的 1-3->4;4-6->7。
我们通过预处理将可能的next值升序放在一个vector容器内,注意一点要把4444444444放进去,<-当输入1e9时。
给定l,r;我们通过lower_bound(it,it,l),查找l在容器中的位置。一步步往下去取区间,注意特判临界情况,即第一个区间的右边界>r,最后一个区间左边界是不是要全拿。

时间复杂度 O(N)

Code

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll;

inline int read() {
    int s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

const int maxn = 1e9 + 3;
vector<ll> p; //存放4 7 44 47 74...
queue<ll> que;

void init() {
    p.push_back(4);
    p.push_back(7);
    que.push(4);
    que.push(7);
    while (que.size()) {
        ll x = que.front();
        que.pop();
        ll tmp1 = (x << 3) + (x << 1) + 4, tmp2 = (x << 3) + (x << 1) + 7;
        if (tmp1 < maxn) {
            p.push_back(tmp1);
            que.push(tmp1);
        }
        if (tmp2 < maxn) {
            p.push_back(tmp2);
            que.push(tmp2);
        }
    }
    p.push_back(4444444444);
}

int main() {
    init();
    int l = read(), r = read();
    int pos = lower_bound(p.begin(), p.end(), l) - p.begin();
    ll sum = 0;
    if (p[pos] <= r) sum = (p[pos] - l + 1) * p[pos];
    else {
        sum = (r - l + 1) * p[pos];
        printf("%lld\n", sum);
        return 0;
    }
    for (++pos; p[pos] <= r; ++pos)  sum += (p[pos] - p[pos - 1]) * p[pos];
    if (p[pos] > r && p[pos - 1] != r)   //r不在边界上  7777
        sum += (r - p[pos - 1]) * p[pos];
    printf("%lld\n", sum);
    return 0;
}
全部评论
这就是大佬吗,爱了爱了
1 回复 分享
发布于 2020-04-08 11:21

相关推荐

听说改名字就能收到offer哈:Radis写错了兄弟
点赞 评论 收藏
分享
勤奋努力的椰子这就开摆:美团骑手在美团工作没毛病
投递美团等公司10个岗位
点赞 评论 收藏
分享
2 收藏 评论
分享
牛客网
牛客企业服务