• 美团点评CodeM资格赛部分问题题解


    优惠券

    注意的点是任何时刻一张优惠券只能存在一个,如果同一张优惠券还没被使用又购入,就看上一次购入与该次购入之间有没有问号可以替代使用,使用优惠券同理。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <string>
    #include <unordered_map>
    #include <unordered_set>
    #include <set>
    
    using namespace std;
    
    int len;
    
    int main()
    {
        string line;
        mywhile:
        while (cin >> len) {
            getchar();
            unordered_set<string> cards;
            set<int> unknowns;
            unordered_map<string, int> last_out, last_in;
            for (int i = 1; i <= len; ++i) {
                getline(cin, line);
                if (line[0] == 'I') {
                    string x = line.substr(2);
                    if (cards.count(x) == 0) {
                        cards.insert(x);
                    } else {
                        int last_t = ( last_in.count(x) == 1 ? last_in[x] : 0 );
                        set<int>::iterator it = unknowns.lower_bound(last_t);
                        if (it == unknowns.end()) {
                            cout << i << endl;
                            goto mywhile;
                        } else {
                            unknowns.erase(it);
                        }
                    }
                    last_in[x] = i;
                } else if (line[0] == 'O') {
                    string x = line.substr(2);
                    if (cards.count(x) == 1) {
                        cards.erase(x);
                    } else {
                        int last_t = ( last_out.count(x) == 1 ? last_out[x] : 0 );
                        set<int>::iterator it = unknowns.lower_bound(last_t);
                        if (it == unknowns.end()) {
                            cout << i << endl;
                            goto mywhile;
                        } else {
                            unknowns.erase(it);
                        }
                    }
                    last_out[x] = i;
                } else if (line == "?") {
                    unknowns.insert(i);
                }
            }
            cout << -1 << endl;
        }
        return 0;
    }
    
    

    送外卖

    https://www.nowcoder.com/question/next?pid=5513596&qid=104903&tid=8742003

    挺好的题。我的做法是建图——将每个小区抽象为图节点,小区i有两条出边,出边a到达小区i+a[i],出边b到达小区i+b[i](如果到达的小区不存在就不用加边)。问题即转化为在一个有向图中,判断节点0n-1是否可达,如果可达,每次尽量走边a(这样字典序才是最小的)。

    • 预处理好每个节点到n-1是否可达的信息,这样在构造路径的时候好快速选择正确的下一跳。为此,在建图的时候同时记录某个节点的前继节点,然后从节点n-1出发跑一遍BFS即可。
    • 从起点0出发构造路径:
      • 如果当前节点已经是终点了,返回结果;
      • 否则看走a到达的下一跳与终点是否可达,是的话就走a,否则走b
      • 如果选择的下一跳已经被走过,说明构成了一个环,这意味着字典序最小的字符串无限长。
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <string>
    #include <unordered_map>
    #include <unordered_set>
    #include <set>
    #include <queue>
    
    using namespace std;
    
    int n;
    
    void solve(vector<int>& a, vector<int>& b, vector<vector<bool>>& adj, vector<vector<int>>& prevs)
    {
        int s = 0, t = n - 1;
        vector<bool> can(n, false);
        can[n-1] = true;
        // BFS
        queue<int> q;
        q.push(t);
        while (!q.empty()) {
            int cur = q.front(); q.pop();
            for (int i = 0; i < prevs[cur].size(); ++i) {
                if (!can[prevs[cur][i]]) {
                    can[prevs[cur][i]] = true;
                    q.push( prevs[cur][i] );
                }
            }
        }
    
        // go from s
        int cur = s;
        if (!can[cur]) {
            cout << "No solution!" << endl;
            return;
        }
        vector<bool> vis(n, false);
        vis[cur] = true;
        string res = "";
        while (cur != t) {
            for (int i = 0; i < adj[cur].size(); ++i) {
                //pair<char, int> p = adj[cur][i];
                bool select = adj[cur][i];
                int to;
                char select_;
                if (select == true) {
                    to = cur + a[cur];
                    select_ = 'a';
                } else {
                    to = cur + b[cur];
                    select_ = 'b';
                }
                if (to == t) {
                    res += select_;
                    cout << res << endl;
                    return;
                }
                if (can[to]) {
                    if (vis[to]) {
                        cout << "Infinity!" << endl;
                        return;
                    }
                    vis[to] = true;  // 一开始由于这句没加上,导致爆空间,浪费了四次提交机会。。
                    res += select_;
                    cur = to;
                    break;
                }
            }
        }
    }
    
    int main()
    {
        cin >> n;
    
        vector<int> a(n);
        vector<int> b(n);
        vector<vector<bool>> adj(n);
        vector<vector<int>> prevs(n);
    
        for (int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
            int to = i + a[i];
            if (to >= 0 && to < n) {
                adj[i].push_back(true);
                prevs[to].push_back(i);
            }
        }
        for (int i = 0; i < n; ++i) {
            scanf("%d", &b[i]);
            int to = i + b[i];
            if (to >= 0 && to < n) {
                adj[i].push_back(false);
                prevs[to].push_back(i);
            }
        }
        solve(a, b, adj, prevs);
        return 0;
    }
    
    

    数码

    https://www.nowcoder.com/question/next?pid=5513596&qid=104907&tid=8742003

    • 首先,计算1~n的结果,则区间[l, r]的结果等于[1, r]减去[1, l-1]
    • 核心是计算最高位为i (1 <= i <= 9)的约数在[1, n]出现的次数
    • i = 2为例,ans[2] = cnt(2) + cnt(20) + cnt(21) + ... + cnt(29) + ... + cnt(200) + cnt(201) + ... + cnt(不超过n的最高位为2的最大的数), 其中cnt(x)表示[1,n]中约数为某个数x的数量,它很好计算:cnt(x) = n / x
    • 那么最高位为i时,分别统计长度为1,2,...的数:长度为1是[i,(i+1)-1]; 长度为2是[i*10, (i+1)*10-1]; 长度为3是[i*10^2, (i+1)*10^2-1],以此类推,长度为j[i*10^(j-1), (i+1)*10^(j-1)-1]
    • 于是ans[i] = sum(cnt(x) for x in range(i, i+1)) + sum(cnt(x) for x in range(i*10, (i+1)*10)) + sum(cnt(x) for x in range(i*10^2, (i+1)*10^2)) + ...
    • 但是当n很大时(比如n >= 10^7),枚举量会很大
    • 长度为j时的数量为sum(cnt(x) for x in range(i*10^(j-1)), (i+1)*10^(j-1)) = n/low + n/(low+1) + ... + n/high,其中low = i*10^(j-1), high = (i+1)*10^(j-1) - 1. 注意到随着x的增加,n / x是单调不增的,且当j >= 6时,最大的项n/low = n / (i*10^(j-1)) <= n / (i*10^5) <= 10^4 / i <= 10^4,可见这么多项的值是不多的(都是离散量),最多也就10000个,这就意味着我们可以换个思路-->去枚举每一项值的范围,然后在{n/low, n/(low+1) ... n/high}中找等于该值的项的数量是多少。至于如何找,其实对每一个值可以O(1)找出来,如果对细节处理没把握,保险起见还可以二分查找-->由于这个数列是单调的,分别找到等于这个值的最左边的项和最右边的项即可
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    #include <string>
    #include <unordered_map>
    #include <unordered_set>
    #include <set>
    #include <queue>
    
    using namespace std;
    
    long long l, r;
    
    long long compute_single(long long low, long long high, long long n)
    {
        // compute: n/low + n/(low+1) + ... + n/(high)
        if (low > high) return 0;
        long long ret = 0;
        if (high - low <= 1e5) {
            for (long long i = low; i <= high; ++i) {
                ret += (n / i);
            }
        } else {
            long long start = n / high, end = n / low;
            for (long long x = start; x <= end; ++x) {  // how many n/i equals to x
                long long lb = 0, ub = -1;
                // binary search for lb and ub
                // lb
                long long left = low, right = high, mid;
                while (left + 1 < right) {
                    mid = left + (right - left) / 2;
                    if (n / mid <= x) right = mid;
                    else left = mid;
                }
                if (n / left == x) lb = left;
                else if (n / right == x) lb = right;
                // ub
                left = low; right = high;
                while (left + 1 < right) {
                    mid = left + (right - left) / 2;
                    if (n / mid >= x) left = mid;
                    else right = mid;
                }
                if (n / right == x) ub = right;
                else if (n / left == x) ub = left;
                //cout << lb << " " << ub << " " << x << endl;
    
                ret += (ub - lb + 1) * x;
            }
        }
        return ret;
    }
    
    void compute(long long n, vector<long long>& ans)
    {
        if (n == 0) return;
        for (int i = 1; i <= 9; ++i) {  // digit: i
            long long base = 1;
            for (int j = 0; j <= 9; ++j) {  // j+1: the length of number with i the highest digit
                long long low = i * base, high = (i + 1) * base - 1;
                high = min(high, n);
                long long cur = compute_single(low, high, n);
                ans[i] += cur;
                if (high >= n) break;
                base *= 10;
            }
        }
    }
    
    void solve()
    {
        vector<long long> ans_l(10, 0);
        vector<long long> ans_r(10, 0);
        compute(r, ans_r);
        compute(l-1, ans_l);
        for (int i = 1; i <= 9; ++i) {
            cout << (ans_r[i] - ans_l[i]) << endl;
        }
    }
    
    int main()
    {
        cin >> l >> r;
        solve();
        return 0;
    }
    
    
  • 相关阅读:
    【CSS】419- 彻底搞懂word-break、word-wrap、white-space
    【Webpack】418- 深度优化 Webpack 性能,翻倍构建性能
    【React】417- React中componentWillReceiveProps的替代升级方案
    巩固java(二)----JVM堆内存结构及垃圾回收机制
    巩固java(一)----java与对象
    Latex 公式换行问题,(换行,等号对齐)
    Android FoldingLayout 折叠布局 原理及实现(二)
    19.最省钱的app发短信方法
    Android FoldingLayout 折叠布局 原理及实现(一)
    18.app后端如何实现LBS
  • 原文地址:https://www.cnblogs.com/ilovezyg/p/7041647.html
Copyright © 2020-2023  润新知