zoukankan      html  css  js  c++  java
  • 牛客小白月赛33全题解

    小白月赛33题解(全)

    A.字符统计

    题目链接:https://ac.nowcoder.com/acm/contest/11210/A

    直接读入字符串进行遍历即可。用getline()读一行字符串,注意:cin读入后的回车会留在输入缓冲区,getline()的不会,所以在使用cin后,getline()之前需要清空输入缓冲区

    注意行开头空格和空行的特殊处理。

    AC代码:

    #include <iostream>
    
    using namespace std;
    
    string s;
    int r, dc, zf;
    
    int main()
    {
        int t;
        cin >> t;
        getline(cin, s);
        
        while(t--)
        {
            r = dc = zf = 0;
            while(getline(cin, s) && s != "=====")
            {
                r++;
                for(int i = 0; i < s.size(); i++)
                {
                    if(s[i] == ' ' && i) dc++;
                    zf++;
                }
                if(s.size())dc++;
            }
            cout << r << ' ' << dc << ' ' << zf << endl;
        }
        
        return 0;
    }
    

    B.连分数

    题目链接:https://ac.nowcoder.com/acm/contest/11210/B

    对一般的(p/q)(p / q = a + 1 / m, 其中m = q / (p \% q)),然后令分子 = q, 分母 = p % q,继续上述操作,直到$ p % q == 0$时结束操作。

    细节处理:

    1. 没输出一个a,如果p、q更新后可以进行操作,则输出“+1/{”,并且记录输出的’{‘的数量
    2. 如果p、q更新后不可进行操作,即(p \% q == 0),则说明到最后一个数了,不用输出大括号,并且下一轮会结束循环
    3. 最后输出等量的’}‘。

    AC代码:

    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    int t, p, q;
    
    int main()
    {
        cin >> t;
        
        while(t--)
        {
            scanf("%d%d", &p, &q);
            printf("%d/%d = ", p, q);
            int kh = 0;
            while(q)
            {
                printf("%d", p / q);
                int t = p % q;
                p = q, q = t;
                if(q)
                {
                    if(p % q != 0)
                    {
                        printf("+1/{");
                        kh++;
                    }
                    else printf("+1/");
                }
            }
            while(kh--) printf("}");
            puts("");
        }
        return 0;
    }
    

    C.挪酒瓶

    题目链接:https://ac.nowcoder.com/acm/contest/11210/C

    参考题解:https://www.nowcoder.com/discuss/643079https://blog.nowcoder.net/n/efd1e10b345a42e1ae0231fc4ae9e7b4

    对于一个有 n 个元素的置换群p,从最后一个 a往前开始换,则费用为 ((w_a+w_b)+(w_a+w_c)+...=(n−2)w_a+sum_{iin p}w_i)

    最优的策略则是选择较小的 (w_a) 开始换。

    对于多个置换群而言,单独内部交换并不是最优解,可以把目前最轻的酒瓶拿进来替换,最后再换回去。令置换群 p 内最轻重量为 (m_p=min_{iin p}wi),令全部的酒里面最轻重量为 (m_{all})。这个策略的花费为 (2(m_{all}+m_p)+(n−2)m_{all}+sum_{iin p}w_i - m_p + m_{all})(交换两次最小值花费:(2(m_{all}+m_p)),交换后的所有费用和为:(sum_{iin p}w_i - m_p + m_{all}))

    最后对于每一个置换群,考虑两个 Case 如下,都选择最优即可:

    • ((n−2)w_p+sum_{iin p}w_i)
    • (2(m_{all}+m_p)+(n−2)m_{all}+∑_{iεp}w_i - m_p + m_{all}) = ((n + 1)*m_{all} + m_p + sum_{iin p}w_i)

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int N = 1e5+10;
    
    int a[N], w[N];
    bool st[N];
    
    int main()
    {
        int t;
        cin >> t;
        while(t--)
        {
            memset(st, 0, sizeof st);
            int n, ans = 0, min_all = 1e9;
            cin >> n;
            for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
            for(int i = 1; i <= n; i++)
            {
                scanf("%d", &w[i]);
                //找出全局最小值
                min_all = min(min_all, w[i]);
            }
            
            for(int i = 1; i <= n; i++)
                if(!st[i])
                {
                    int cnt = 0, minv = 1e9;
                    //求一个置换群
                    for(int j = i; !st[j]; j = a[j])
                    {
                        cnt++;
                        ans += w[j];
                        minv = min(minv, w[j]);
                        st[j] = true;
                    }
                    //最优解更新
                    ans += min((cnt - 2) * minv, (cnt + 1) * min_all + minv);
                }
            cout << ans << endl;
        }
        
        return 0;
    }
    

    D.购物

    题目链接:https://ac.nowcoder.com/acm/contest/11210/D

    将物品名称映射到数字,用数组记录每一个物品的数量,然后遍历每个人,将其没有的物品的数量-1,最后遍历计算物品数量大于0的个数。

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <unordered_map>
    #include <cstring>
    
    using namespace std;
    
    const int N = 110;
    
    int t, s, n;
    int a[N];
    
    int main()
    {
        cin >> t;
        
        while(t--)
        {
            memset(a, 0, sizeof a);
            unordered_map<string, int> goods;
            string str;
            int x, idx = 0;
            scanf("%d%d", &s, &n);
            for(int i = 0; i < s; i++)
            {
                cin >> str >> x;
                goods.insert({str, idx++});
                a[goods[str]] = x;
            }
            //for(int i = 0; i < idx; i++) cout << a[i] << ' ';
            //puts("");
            for(int i = 0; i < n; i++)
            {
                bool has[N];
                memset(has, false, sizeof has);
                cin >> x;
                for(int j = 0; j < x; j++)
                {
                    cin >> str;
                    has[goods[str]] = true;
                }
                for(int i = 0; i < idx; i++)
                    if(!has[i]) a[i]--;
            }
            int ans = 0;
            for(int i = 0; i < idx; i++)
                if(a[i] > 0) ans++;
            if(ans) cout << ans << endl;
            else puts("Need to be lucky");
        }
        return 0;
    }
    

    E.喝可乐

    题目链接:https://ac.nowcoder.com/acm/contest/11210/E

    直接枚举每种可乐买多少瓶的所有情况,对每种情况计算能喝到的瓶数,记录最大值即可。

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int t, n, a, b;
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            int ans = 0;
            cin >> n >> a >> b;
            for(int i = 0; i <= n; i++)
            {
                int Max = n, an = i, bn = n - i;
                while(an >= a || bn >= b)
                {
                    if(an >= a)
                    {
                        Max += an / a;
                        bn += an / a;
                        an = an % a;
                    }
                    if(bn >= b)
                    {
                        Max += bn / b;
                        an += bn / b;
                        bn = bn % b;
                    }
                }
                ans = max(Max, ans);
            }
            cout << ans << endl;
        }
        return 0;
    }
    

    F.天旋地转

    题目链接:https://ac.nowcoder.com/acm/contest/11210/F

    模拟,把坐标系的四种不同情况下的移动偏移量存下来,然后按步骤一步一步操作即可。

    注意在计算逆时针旋转时,标记坐标轴方向的数字fx需要变为非负数

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <unordered_map>
    
    #define x first
    #define y second;
    
    using namespace std;
    
    typedef long long LL;
    typedef pair<int, int> PII;
    
    int t, n;
    PII Move[4][4] = {{{0, 1}, {-1, 0}, {1, 0}, {0, -1}},
    {{-1, 0}, {0, -1}, {0, 1}, {1, 0}},
    {{0, -1}, {1, 0}, {-1, 0}, {0, 1}},
    {{1, 0}, {0, 1}, {0, -1}, {-1, 0}}};
    unordered_map<char, int> f {{'w', 0}, {'a', 1}, {'d', 2}, {'s', 3}};
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            LL xx = 0, yy = 0, fx = 0, k;
            cin >> n;
            while(n--)
            {
                char c;
                cin >> c >> k;
                if(c == 'r') fx = (fx + k) % 4;
                else if(c == 'l') fx = ((fx - k) % 4 + 4) % 4;
                else xx += k * Move[fx][f[c]].x, yy += k * Move[fx][f[c]].y;
                //cout << fx << ' ' << xx << ',' << yy << endl;
            }
            cout << xx << ' ' << yy << endl;
        }
        return 0;
    }
    

    G.切圈圈

    题目链接:https://ac.nowcoder.com/acm/contest/11210/G

    首先预处理出前缀和数组。可以知道性质:前缀和数组中相等的两点,之间的区间和一定是0。题目已经给出整个数组的和为0,那么只要确定数组中的一段和为0的区间,则另外一段(包含首尾的那一段)也就一定区间和为0了。所以可以直接在一维线性结构上进行求解。

    当已经确定了一个区间和为0的区间时,这区间的端点前缀和数组的值一定相等。如果该区间内可以再分,由性质可得,区间内的切分点的前缀和数组值一定和两个端点的值相等。(环的另外一段也是如此)

    由此可以进一步推出:最后分割出来的区间,所有端点的前缀和数组值一定相等。

    所以,只要求出前缀和数组值相等的最大数量,即为答案。

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <map>
    
    using namespace std;
    
    const int N = 10010;
    
    int t, n;
    int a[N];
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            cin >> n;
            int ans = 0;
            map<int, int> m;
            for(int i = 1; i <= n; i++)
            {
                scanf("%d", &a[i]);
                //预处理前缀和数组
                a[i] += a[i - 1];
                //记录值为a[i]的点有多少个
                m[a[i]]++;
                ans = max(ans, m[a[i]]);
            }
            cout << ans << endl;
        }
        return 0;
    }
    

    H.货物运输

    题目链接:https://ac.nowcoder.com/acm/contest/11210/H

    根据题目建有向图,然后直接套最短路模板即可。

    关于建图,难点在处理好边权:

    边权:w[i] = c * min(f, d) + max(0, f - d) * cc

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 110;
    
    int t, n, m, s, ee, f;
    int a, b, c, d, cc;
    int h[N], e[N * N], ne[N * N], idx;
    LL w[N * N], dist[N];
    bool st[N];
    
    void add(int a, int b, LL c)
    {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
    }
    
    void dij(int u)
    {
        dist[u] = 0;
        
        for(int i = 0; i < n; i++)
        {
            int k = -1;
            for(int j = 1; j <= n; j++)
                if(!st[j] && (k == -1 || dist[k] > dist[j]))
                    k = j;
            
            for(int j = h[k]; j != -1; j = ne[j])
            {
                dist[e[j]] = min(dist[e[j]], dist[k] + w[j]);
            }
            
            st[k] = true;
        }
    }
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            idx = 0;
            memset(h, -1, sizeof h);
            memset(dist, 0x3f, sizeof dist);
            memset(st, false, sizeof st);
            scanf("%d%d%d%d%d", &n, &m, &s, &ee, &f);
            for(int i = 0; i < m; i++)
            {
                scanf("%d%d%d%d%d", &a, &b, &c, &d, &cc);
                //注意d、f的大小
                add(a, b, (LL)c * min(d, f) + (LL)max(0, f - d) * cc);
            }
            dij(s);
            //for(int i = 1; i <= n; i++)
            //    cout << i << ':' << dist[i] << endl;
            printf("%lld
    ", dist[ee]);
        }
        
        return 0;
    }
    

    I.三角尼姆

    题目链接:https://ac.nowcoder.com/acm/contest/11210/I

    首先手动模拟发现:N = 1时Alice必赢,此时一共有1个空位;N = 2时Alice必赢,此时一共有3个空位;N = 3时Bob必赢,此时一共有6个空位;N = 4时Bob必赢,此时一共有10个空位。。。。。。

    大胆猜测出:当空位总数为偶数个是,先手必赢,否则先手必输

    经代码检验猜测正确。

    证明:最后输的条件一定是只剩1个空位,往前推一下就是倒数第二次放棋子一定是3个空位,即最后面对的必输局面是剩下奇数个空位

    由:奇数 - 奇数 = 偶数,偶数 - 奇数 = 奇数,且每次减少空位一定是1或者3(奇数)

    可得:剩余空位数一定是以“奇偶奇偶...”或者“偶奇偶奇...”的顺序出现的

    进一步可得:如果玩家第一次面对的是偶数个空位,则一直是面对偶数个空位,则一定不会碰到奇数的情况,就一定不会输

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int t, n;
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            cin >> n;
            n = (1 + n) * n / 2;
            if(n & 1) puts("Alice");
            else puts("Bob");
        }
        return 0;
    }
    

    J.线段的交

    题目链接:https://ac.nowcoder.com/acm/contest/11210/J

    分两步:判断线段是否相交、求面积。

    解法一:利用向量叉积判断线段相交及求四边形面积。

    线段相交

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    double x1, yy1, x2, y2, x3, y3, x4, y4;
    
    struct Point
    {
        double x, y;
    }p1, p2, q1, q2;
    
    double cj(Point p, Point q)
    {
        return p.x * q.y - q.x * p.y;
    }
    
    double area(Point p1, Point p2, Point q1)
    {
        Point xl1 = {p2.x - p1.x, p2.y - p1.y};
        Point xl2 = {q1.x - p1.x, q1.y - p1.y};
        return cj(xl1, xl2);
    }
    
    int main()
    {
        scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &x1, &yy1, &x2, &y2, &x3, &y3, &x4, &y4);
        p1 = {x1, yy1}, p2 = {x2, y2}, q1 = {x3, y3}, q2 = {x4, y4};
        
        double s1 = area(p1, p2, q1);
        double s2 = area(p1, p2, q2);
        double s3 = area(q1, q2, p1);
        double s4 = area(q1, q2, p2);
        if(s1 * s2 <= 0 && s3 * s4 <= 0) printf("%.8f
    ", (fabs(s1) + fabs(s2)) / 2);
        else cout << 0 << endl;
        
        return 0;
    }
    

    解法二:利用快速排斥实验和跨立实验判断直线相交,在用向量叉积求四边形面积

    WA代码(case通过率92%,bug未知):

    #include <iostream>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    double x1, yy1, x2, y2, x3, y3, x4, y4;
    
    struct Point
    {
        double x, y;
    }p1, p2, q1, q2;
    //快速排斥实验
    bool kspc(Point p1, Point p2, Point q1, Point q2)
    {
        double minRx = min(p1.x, p2.x), minRy = min(p1.y, p1.y);
        double maxRx = max(p1.x, p2.x), maxRy = max(p1.y, p2.y);
        double minTx = min(q1.x, q2.x), minTy = min(q1.y, q2.y);
        double maxTx = max(q1.x, q2.x), maxTy = max(q1.y, q2.y);
        double minFx = max(minRx, minTx), minFy = max(minRy, minTy);
        double maxFx = min(maxRx, maxTx), maxFy = min(maxRy, maxTy);
        return (minFx <= maxFx && minFy <= maxFy);
    }
    //求向量叉积
    double cj(Point p, Point q)
    {
        return p.x * q.y - q.x * p.y;
    }
    //跨立实验
    bool kl(Point p1, Point p2, Point q1, Point q2)
    {
        Point xl1 = {p1.x - q1.x, p1.y - q1.y}, xl2 = {q2.x - q1.x, q2.y - q1.y}, xl3 = {p2.x - q1.x, p2.y - q1.y};
        Point xl4 = {q1.x - p1.x, q1.y - p1.y}, xl5 = {p2.x - p1.x, p2.y - p1.y}, xl6 = {q2.x - p1.x, q2.y - p1.y};
        return (cj(xl1, xl2) * cj(xl2, xl3) >= 0 && cj(xl4, xl5) * cj(xl5, xl6) >= 0);
    }
    //求面积
    void area()
    {
        /*
        double s1 = fabs(p1.x * p2.y - p2.x * p1.y + p2.x * q1.y - q1.x * p2.y + q1.x * p1.y - p1.x * q1.y) / 2;
        double s2 = fabs(p1.x * p2.y - p2.x * p1.y + p2.x * q2.y - q2.x * p2.y + q2.x * p1.y - p1.x * q2.y) / 2;
        */
        Point xl1 = {p2.x - p1.x, p2.y - p1.y};
        Point xl2 = {q1.x - p1.x, q1.y - p1.y};
        Point xl3 = {q2.x - p1.x, q2.y - p1.y};
        printf("%.8lf
    ", (fabs(cj(xl1, xl2)) + fabs(cj(xl1, xl3))) / 2);
    }
    
    int main()
    {
        scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &x1, &yy1, &x2, &y2, &x3, &y3, &x4, &y4);
        p1 = {x1, yy1}, p2 = {x2, y2}, q1 = {x3, y3}, q2 = {x4, y4};
        
        if(kspc(p1, p2, q1, q2) && kl(p1, p2, q1, q2)) area();
        else cout << 0 << endl;
        
        return 0;
    }
    
  • 相关阅读:
    《研磨设计模式》阅读摘要
    心电图
    nodejs
    自动化测试
    Hook技术
    热修复原理
    理解ClassLoader
    Dalvik和ART
    Java虚拟机
    理解WindowManagerService
  • 原文地址:https://www.cnblogs.com/grain-rain/p/14693175.html
Copyright © 2011-2022 走看看