50道牛客题

1.小笨的数字权值

以下是针对该问题的C++和C语言解决方案。解决方案的核心思路是预处理出每个数字的最大权值和,然后对每个查询直接输出结果。

问题分析

  • 权值定义:一个数字的权值是其正因子个数。
  • 目标:将给定的正整数 ( x ) 分解为若干大于1的数字(可以不分解),使得这些数字的乘积等于 ( x ),且它们的权值之和最大化。
  • 关键观察
    • 不分解时,权值之和为 ( d(x) )(即 ( x ) 的因子个数)。
    • 分解时,将 ( x ) 分解为两个因子 ( a \times b )(( a, b \geq 2 )),则权值之和为 ( f(a) + f(b) ),其中 ( f ) 表示该数字的最大权值和(包括进一步分解的可能)。
    • 通过动态规划,从小到大计算每个数字的最大权值和 ( f(x) ),利用 ( f(x) = \max(d(x), \max_{2 \leq a \leq \sqrt{x}} { f(a) + f(b) } )(其中 ( b = x/a ))。
  • 预处理
    1. 使用筛法预处理每个数字的因子个数 ( d(x) )。
    2. 初始化 ( f(x) = d(x) )(不分解的情况)。
    3. 使用动态规划,枚举因子 ( i ) 和 ( j ),更新 ( f[i \times j] = \max(f[i \times j], f[i] + f[j]) )。

C++ 解决方案

#include <iostream>
#include <algorithm>
using namespace std;

const int max_n = 200000;
int d[max_n + 1];
int f[max_n + 1];

int main() {
    // 预处理因子个数 d
    for (int i = 1; i <= max_n; i++) {
        for (int j = i; j <= max_n; j += i) {
            d[j]++;
        }
    }

    // 初始化 f(x) = d(x)
    for (int i = 1; i <= max_n; i++) {
        f[i] = d[i];
    }

    // 动态规划:更新 f[k] 为分解后的最大权值和
    for (int i = 2; i <= max_n; i++) {
        for (int j = 2; j <= max_n / i; j++) {
            int k = i * j;
            if (f[i] + f[j] > f[k]) {
                f[k] = f[i] + f[j];
            }
        }
    }

    // 处理查询
    int T;
    scanf("%d", &T);
    while (T--) {
        int x;
        scanf("%d", &x);
        printf("%d\n", f[x]);
    }
    return 0;
}

C 语言解决方案

#include <stdio.h>

#define max_n 200000

int d[max_n + 1];
int f[max_n + 1];

int main() {
    // 预处理因子个数 d
    for (int i = 1; i <= max_n; i++) {
        for (int j = i; j <= max_n; j += i) {
            d[j]++;
        }
    }

    // 初始化 f(x) = d(x)
    for (int i = 1; i <= max_n; i++) {
        f[i] = d[i];
    }

    // 动态规划:更新 f[k] 为分解后的最大权值和
    for (int i = 2; i <= max_n; i++) {
        for (int j = 2; j <= max_n / i; j++) {
            int k = i * j;
            if (f[i] + f[j] > f[k]) {
                f[k] = f[i] + f[j];
            }
        }
    }

    // 处理查询
    int T;
    scanf("%d", &T);
    while (T--) {
        int x;
        scanf("%d", &x);
        printf("%d\n", f[x]);
    }
    return 0;
}

代码说明

  1. 预处理因子个数
    • 使用筛法,对于每个 ( i ),将其倍数 ( j ) 的因子个数 ( d[j] ) 增加1。
  2. 初始化 ( f ) 数组
    • 初始时,每个数字的 ( f(x) ) 为其因子个数 ( d(x) )(即不分解的权值和)。
  3. 动态规划更新
    • 对于每个数字 ( i )(从2开始),枚举另一个数字 ( j )(从2开始),计算乘积 ( k = i \times j )。
    • 如果 ( f[i] + f[j] > f[k] ),则更新 ( f[k] ) 为 ( f[i] + f[j] )(表示将 ( k ) 分解为 ( i ) 和 ( j ) 更优)。
  4. 处理查询
    • 对于每组查询,直接输出预处理得到的 ( f[x] )。

复杂度分析

  • 预处理 ( d ) 数组:时间复杂度 ( O(n \log n) ),其中 ( n = 200000 )。
  • 动态规划:时间复杂度 ( O(n \log n) ),因为内层循环次数是调和级数。
  • 查询:每次查询 ( O(1) ),总体 ( O(T) )。
  • 总复杂度:预处理 ( O(n \log n) ),查询 ( O(T) ),满足题目约束(( n \leq 200000 ), ( T \leq 10000 ))。

2. 小红的子序列逆序对

问题分析

我们需要计算一个数组中所有子序列的逆序对数量之和。逆序对的定义是:在一个子序列中,如果前面的元素大于后面的元素,则这两个元素构成一个逆序对。由于子序列可以不连续,我们需要考虑所有可能的子序列。

关键思路

  1. 逆序对的贡献:对于数组中的任意两个元素 (a_i) 和 (a_j)((i < j)),如果 (a_i > a_j),则这对元素在所有包含它们的子序列中都会贡献一个逆序对。
  2. 子序列包含这对元素的概率:对于任意子序列,包含 (a_i) 和 (a_j) 的概率是 ( \frac{1}{2^{j-i+1}} ),但实际上更准确的计算是:对于所有子序列,包含 (a_i) 和 (a_j) 的子序列数量是 (2^{n - (j - i + 1)})。
  3. 总逆序对数量:对于所有满足 (i < j) 且 (a_i > a_j) 的 ((i, j)) 对,计算它们对所有子序列的贡献之和。

数学推导

对于每一对 ((i, j)) 满足 (i < j) 且 (a_i > a_j),它们在所有子序列中的贡献是:

  • 包含 (a_i) 和 (a_j) 的子序列数量为 (2^{n - 2})(因为除了 (a_i) 和 (a_j) 之外的其他 (n - 2) 个元素可以任意选或不选)。
  • 因此,这对 ((i, j)) 的贡献是 (2^{n - 2})。

因此,总逆序对数量之和为:
[ \text{Total} = \sum_{i < j, a_i > a_j} 2^{n - 2} ]

优化计算

我们需要高效地计算满足 (i < j) 且 (a_i > a_j) 的对数。这可以通过归并排序或**树状数组(Fenwick Tree)**在 (O(n \log n)) 时间内完成。

C++ 解决方案

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MOD = 1e9 + 7;

// 树状数组类
class FenwickTree {
private:
    vector<int> tree;
    int size;

public:
    FenwickTree(int n) : size(n), tree(n + 1, 0) {}

    void update(int idx, int delta) {
        while (idx <= size) {
            tree[idx] += delta;
            idx += idx & -idx;
        }
    }

    int query(int idx) {
        int res = 0;
        while (idx > 0) {
            res += tree[idx];
            idx -= idx & -idx;
        }
        return res;
    }
};

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }

    // 离散化
    vector<int> temp = a;
    sort(temp.begin(), temp.end());
    temp.erase(unique(temp.begin(), temp.end()), temp.end());
    for (int i = 0; i < n; ++i) {
        a[i] = lower_bound(temp.begin(), temp.end(), a[i]) - temp.begin() + 1;
    }

    // 计算逆序对数量
    FenwickTree ft(temp.size());
    long long inv_count = 0;
    for (int i = n - 1; i >= 0; --i) {
        inv_count += ft.query(a[i] - 1);
        ft.update(a[i], 1);
    }

    // 计算总贡献
    long long power = 1;
    for (int i = 0; i < n - 2; ++i) {
        power = (power * 2) % MOD;
    }
    long long total = (inv_count % MOD) * (power % MOD) % MOD;

    cout << total << endl;

    return 0;
}

C 语言解决方案

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MOD 1000000007
#define MAXN 100005

typedef struct {
    int val;
    int idx;
} Pair;

int cmp(const void *a, const void *b) {
    return ((Pair *)a)->val - ((Pair *)b)->val;
}

int unique(Pair *arr, int n) {
    if (n == 0) return 0;
    int j = 0;
    for (int i = 1; i < n; i++) {
        if (arr[i].val != arr[j].val) {
            arr[++j] = arr[i];
        }
    }
    return j + 1;
}

int fenwick[MAXN];

void update(int idx, int delta, int size) {
    while (idx <= size) {
        fenwick[idx] += delta;
        idx += idx & -idx;
    }
}

int query(int idx) {
    int res = 0;
    while (idx > 0) {
        res += fenwick[idx];
        idx -= idx & -idx;
    }
    return res;
}

int main() {
    int n;
    scanf("%d", &n);
    int a[MAXN];
    Pair temp[MAXN];
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        temp[i].val = a[i];
        temp[i].idx = i;
    }

    // 离散化
    qsort(temp, n, sizeof(Pair), cmp);
    int m = unique(temp, n);
    for (int i = 0; i < n; i++) {
        int left = 0, right = m - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (temp[mid].val == a[i]) {
                a[i] = mid + 1;
                break;
            } else if (temp[mid].val < a[i]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }

    // 计算逆序对数量
    memset(fenwick, 0, sizeof(fenwick));
    long long inv_count = 0;
    for (int i = n - 1; i >= 0; i--) {
        inv_count += query(a[i] - 1);
        update(a[i], 1, m);
    }

    // 计算总贡献
    long long power = 1;
    for (int i = 0; i < n - 2; i++) {
        power = (power * 2) % MOD;
    }
    long long total = (inv_count % MOD) * (power % MOD) % MOD;

    printf("%lld\n", total);

    return 0;
}

代码说明

  1. 离散化:将数组元素映射到连续的整数范围,以便使用树状数组。
  2. 树状数组:用于高效计算逆序对数量。
  3. 逆序对计算:从后往前遍历数组,利用树状数组查询当前元素之前比它小的元素数量。
  4. 贡献计算:总逆序对数量乘以 (2^{n-2}) 并对结果取模。

复杂度分析

  • 离散化:(O(n \log n))。
  • 逆序对计算:(O(n \log n))。
  • 总复杂度:(O(n \log n)),适用于 (n \leq 10^5)。

3. 小美的彩带

参考链接:
美团2024年秋招第一场笔试(小美的彩带题解)-优快云博客

#include <bits/stdc++.h>

using namespace std;

  

#define all(x) x.begin(),x.end()

  

int n, q, len, m, blo;

char op;

int length;

int lisan[200005];

int a[400005], ans[200005];

int mp[200005], siz;

struct stuct {

    int l, r, idx;

    bool operator<(const stuct& x) const {  //如果超时了可以试试这种

        if (l / blo != x.l / blo) return l < x.l;

        return (l / blo) & 1 ? r > x.r : r < x.r;

    }

} b[200005];

int get(int v) {    //离散的具体实现方式因人而异

    return lower_bound(lisan + 1, lisan + 1 + m, v) - lisan;

}

  

int main() {

    scanf("%d %d", &n, &q);

    for (int i = 1; i <= n; ++i) {

        scanf("%d", &a[i]);

        lisan[i] = a[i];

    }

    sort(lisan + 1, lisan + 1 + n);

    m = unique(lisan + 1, lisan + 1 + n) - lisan;

    --m;

    blo = sqrt(2 * n) * 1.5;

    for (int i = 1; i <= n; ++i) {  //离散化

        a[i] = get(a[i]);

        a[i + n] = a[i];

    }

    scanf("\n");

    int l = 1, r = 2 * n, len =

                       0;  //维护一下l,r才知道彩带边缘的下标

    for (int i = 1; i <= q; ++i) {

        scanf("%c %d\n", &op, &length);

        if (length >= n) {

            ans[i] = m;

            length %= n;

            if (op == 'L') {

                l += length;

                if (l > n)l -= n;

            } else {

                r -= length;

                if (r <= n)r += n;

            }

            continue;

        }

        if (op == 'L') {

            ++len;

            b[len].l = l, b[len].r = l + length - 1, b[len].idx = i;

            l += length;

            if (l > n)l -= n;

        } else {

            ++len;

            b[len].l = r - length + 1, b[len].r = r, b[len].idx = i;

            r -= length;

            if (r <= n)r += n;

        }

    }

    sort(b + 1, b + 1 + len);

    l = 0, r = 0;

    for (int i = 1; i <= len; ++i) {

        while (r < b[i].r) {

            ++r;

            if (mp[a[r]] == 0)siz++;

            mp[a[r]]++;

        }

        while (r > b[i].r) {

            mp[a[r]]--;

            if (mp[a[r]] == 0)siz--;

            --r;

        }

        while (l < b[i].l) {

            mp[a[l]]--;

            if (mp[a[l]] == 0)--siz;

            ++l;

        }

        while (l > b[i].l) {

            --l;

            if (mp[a[l]] == 0)++siz;

            mp[a[l]]++;

        }

        ans[b[i].idx] = siz;

    }

    for (int i = 1; i <= q; ++i) printf("%d\n", ans[i]);

    return 0;

}

4. 小美和大富翁

// Created by YuanGodFollower 
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 10;
const int INF = 1e9 + 10;

vector<int> cards = {1,2,3,4};
bool vis[5];
int a[N];

ll dfs(int bias, int targ, int cur, ll val)
{
    if(cur > targ) return -1;
    if(cur == targ) return val;

    ll ret = -1;
    for(auto&& ca : cards)
    {
        if(!vis[ca])
        {
            if(val + a[bias + cur + ca] < 0) continue;
            vis[ca] = 1;
            ret = max(ret, dfs(bias, targ, cur + ca, val + a[bias + cur + ca]));
            vis[ca] = 0;
        }
    }
    return ret;
}

int main()
{
    cin.tie(0)->sync_with_stdio(false);
    int n;cin>>n;
    for(int i = 1;i <= n;i++) cin>>a[i];
    ll ans = 0;
    for(int i = 0;i < n;i+=10)
    {
        int targ = min(10,n - i);
        ans = dfs(i, targ, 0, ans);
        if(ans == -1)
        {
            cout<<-1;
            return 0;
        }
    }
    cout<<ans;
}

5. 小红的数组删除

#include <iostream>

#include <cstring>

#include <vector>

#include <string>

#include <set>

#include <unordered_set>

#include <unordered_map>

#include <cmath>

#include <queue>

#include <iomanip>

#include <algorithm>

  

using namespace std;

  

unordered_map<long long, long long> hashMap;

long long a[200005];

  

void solve() {

    hashMap.clear();

  

    long long n, k, x;

    cin >> n >> k >> x;

    for (long long i = 1; i <= n; i++) {

        cin >> a[i];

        hashMap[a[i]]++;

    }

  

    long long minMiss = 0; // 未出现过的最小非负整数

    while (true) {

        auto it = hashMap.find(minMiss);

        if (it == hashMap.end()) {

            break;

        }

        minMiss++;

    }

  

    long long result = k * minMiss; // 一开始就删除整个数组

    for (long long i = 1; i <= n; i++) {

        long long start = 0;

        long long mex = -1;

        // 注意不能直接删除a[i],因为数组中可能会有重复的值

        auto target = hashMap.find(a[i]);

        if (target->second >= 2) {

            hashMap[a[i]]--;

        } else {

            hashMap.erase(a[i]);

        }

        while (true) {

            auto it = hashMap.find(start);

            if (it == hashMap.end()) {

                mex = start;

                break;

            }

            start++;

        }

        // k代表删除整个数组的花费系数

        // x代表删除单个元素的花费

        result = min(result, i * x + k * mex);

    }

    cout << result << endl;

}

  

int main() {

    std::ios::sync_with_stdio(false);

    cin.tie(0);

    cout.tie(0);

  

    long long T = 1;

    cin >> T;

    while (T--) {

        solve();

    }

  

    return 0;

}

6. 小红的数字删除

#include <iostream>

#include <vector>

using namespace std;

  

int main() {

    int t;

    cin >> t;

    string n;

    while (t--) {

        cin >> n;

        vector<int> vMod(3, 0);//vMod[i]表示原数各位数字中除以3余数为i的个数

        for (char& c : n) {

            ++vMod[(c - '0') % 3];

        }

        int m = (vMod[1] + vMod[2] + vMod[2]) % 3; //原数除以3的余数

        int ans;

        if (m == 0) { //原数为3的倍数

            ans = vMod[0];

            if (ans == n.size()) --ans;

        } else { //原数不是3的倍数

            if (vMod[m] == 0) {   //没有任何一位数字跟原数模3同余

                ans = 0;

            } else {

                ans = vMod[0] + 1; //先删掉同余数字,再删余数为0的

                if (ans == n.size()) --ans;

                if (vMod[m] == 1 && (n[0] - '0') % 3 == m) {

                    for (int i = 1; i < n.size(); ++i) {

                        if (n[i] != '0') break;

                        --ans;

                    }

                }

            }

        }

        cout << ans << endl;

    }

}

7. 小红的数字串

#include <iostream>

#include <string>

using namespace std;

  

int countSubstrings(const string &s, const string &k) {

    int len_s = s.size();

    int len_k = k.size();

    int count = 0;

  

    for (int l = 1; l <= len_s; ++l) {

        if (l < len_k) {

            count += len_s - l + 1;

        } else if (l == len_k) {

            for (int i = 0; i <= len_s - l; ++i) {

                bool less = false;

                for (int j = 0; j < l; ++j) {

                    if (s[i + j] < k[j]) {

                        less = true;

                        break;

                    } else if (s[i + j] > k[j]) {

                        break;

                    }

                }

                if (less) {

                    ++count;

                }

            }

        }

    }

  

    return count;

}

  

int main() {

    string s, k_str;

    cin >> s >> k_str;

    cout << countSubstrings(s, k_str) << endl;

    return 0;

}

8. 小红的01串

#include<bits/stdc++.h>

signed main() {

    std::string s;

    std::cin>>s;

    int ans=0;

    for(int i=0;i<s.size();i++){

        if(s.substr(i,3)=="101"){

            ans++;

            if(s.substr(i+2,3)!="110")s[i+2]='0';

            else s[i+1]='1';

        }

        if(s.substr(i,3)=="010"){

            ans++;

            if(s.substr(i+2,3)!="001")s[i+2]='1';

            else s[i+1]='0';

        }

    }

    std::cout<<ans<<'\n';

    return 0;

}

9. 小红的爆炸串

#include <iostream>
#include <string>
using namespace std;

long long countNonExplodingSubstrings(const string &s, int k) {
    int n = s.size();
    long long count = 0;
    int left = 0;
    int diff_pairs = 0;

    for (int right = 1; right < n; ++right) {
        if (s[right] != s[right - 1]) {
            diff_pairs++;
        }
        while (diff_pairs >= k) {
            if (s[left] != s[left + 1]) {
                diff_pairs--;
            }
            left++;
        }
        count += right - left;
    }

    return count + n; // 加上所有长度为1的子串
}

int main() {
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    cout << countNonExplodingSubstrings(s, k) << endl;
    return 0;
}

10. 小红的拼图

// 横向扫描 + 竖向扫描
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;

// 对于每块拼图而言:在各个方向上的合法的搭档
vector<unordered_set<char>>WLegal = {{'W', '*'}, {'D', 'A', 'W', '*'}, {'A', '*'}, {'D', '*'}};      // {U D L R}
vector<unordered_set<char>>DLegal = {{'W', '*'}, {'S', '*'}, {'S', 'D', 'W', '*'}, {'D', '*'}};
vector<unordered_set<char>>SLegal = {{'D', 'S', 'A', '*'}, {'S', '*'}, {'A', '*'}, {'D', '*'}};
vector<unordered_set<char>>ALegal = {{'W', '*'}, {'S', '*'}, {'A', '*'}, {'W', 'S', 'A', '*'}};

unordered_map<char, vector<unordered_set<char>>>Frame = {{'W', WLegal}, {'D', DLegal}, {'S', SLegal}, {'A', ALegal}};

int main() {
    int n;
    cin >> n;
    int temp_n = n;
    while (n--) {
        int row, col;
        cin >> row >> col;
        vector<vector<char>>Record(row, vector<char>(col));

        int index = 0;
        int temp_row = 0;
        int temp_col = col;
        while (row--) {
            while (temp_col--) {
                cin >> Record[temp_row][index++];
            }
            index = 0;
            temp_col = col;
            temp_row++;
        }

        bool flag = true;
        for (int i = 0; i < Record.size(); i++) {
            if (flag == false)break;
            for (int j = 1; j < Record[i].size(); j+=2) {
                if (flag == false)break;
                if (Record[i][j] == '*')
                    continue;
                if (j == Record[i].size() - 1) {
                    if (Frame[Record[i][j]][2].find(Record[i][j - 1]) ==
                            Frame[Record[i][j]][2].end())flag = false;
                } else {
                    if (Frame[Record[i][j]][2].find(Record[i][j - 1]) ==
                            Frame[Record[i][j]][2].end())flag = false;
                    if (Frame[Record[i][j]][3].find(Record[i][j + 1]) ==
                            Frame[Record[i][j]][3].end())flag = false;
                }
            }
        }

        if (flag == false) {
            cout << "No" << endl;
            continue;
        }

        for (int i = 1; i < Record.size(); i+=2) {
            if (flag == false)break;
            for (int j = 0; j < Record[i].size(); j++) {
                if (flag == false)break;
                if (Record[i][j] == '*')
                    continue;
                if (i == Record.size() - 1) {
                    if (Frame[Record[i][j]][0].find(Record[i - 1][j]) == Frame[Record[i][j]][0].end())flag = false;
                } else {
                    if (Frame[Record[i][j]][0].find(Record[i - 1][j]) == Frame[Record[i][j]][0].end())flag = false;
                    if (Frame[Record[i][j]][1].find(Record[i + 1][j]) == Frame[Record[i][j]][1].end())flag = false;
                }
            }
        }

        if (flag == false) {
            cout << "No" << endl;
        } else {
            cout << "Yes" << endl;
        }
    }

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值