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

    牛客小白月赛32题解

    A.拼三角

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

    相关:暴力枚举(DFS、二进制枚举)

    直接暴力枚举每一种情况,进行判断即可

    AC代码:

    #include <iostream>
    
    using namespace std;
    
    const int N = 10;
    
    int t, a[N];
    
    bool check(int x, int y, int z)
    {
        return (x + y > z && x + z > y && y + z > x);
    }
    
    int main()
    {
        cin >> t;
        
        while(t--)
        {
            for(int i = 0; i < 6; i++) scanf("%d", &a[i]);
            int i = 0;
            for(; i < 4; i++)
                for(int j = i + 1; j < 5; j++)
                    for(int k = j + 1; k < 6; k++)
                    {
                        int x = (1 << 6) - 1, d = 1 << i, e = 1 << j, f = 1 << k;
                        x = x - d - e - f;
                        int b[3];
                        for(int n = 0, m = 0; x; n++, x >>= 1) 
                            if(x & 1) b[m++] = n;
                        if(check(a[i], a[j], a[k]) && check(a[b[0]], a[b[1]], a[b[2]]))
                        {
                            puts("Yes");
                            i = j = k = 10;
                        }
                    }
            if(i != 11) puts("No");
        }
        return 0;
    }
    

    B.相对分子质量

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

    相关:表达式求值、栈

    思路:将化学表达式转化为带括号、加法、乘法的算术表达式,然后将算术表达式转化为后缀表达式进行求值。

    注意细节,特别是数组越界问题。

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <map>
    
    using namespace std;
    
    typedef long long LL;
    
    const int N = 110;
    
    int m, n;
    int x;
    string s;
    map<string, int> zl, op = {{"+", 1}, {"*", 2}, {"(", 3}};
    int t1 = -1, t2 = -1, t3 = -1;
    string stk1[N], stk2[N];
    LL ans[N];
    //转化为后缀表达式
    void to_q(string s[], int idx)
    {
        for(int i = 0; i < idx; i++)
        {
            //cout << s[i] << endl;
            if(s[i] != "(" && s[i] != ")" && s[i] != "+" && s[i] != "*") 
            {
                stk1[++t1] = s[i];
                continue;
            }
            if(s[i] == ")")
            {
                while(stk2[t2] != "(" && t2 > -1) stk1[++t1] = stk2[t2--];
                t2--;
                continue;
            }
            while(t2 > -1 && stk2[t2] != "(" && op[stk2[t2]] >= op[s[i]]) stk1[++t1] = stk2[t2--];
            stk2[++t2] = s[i];
        }
        //if(num.size()) q[++tt] = num;
        while(t2 > -1) stk1[++t1] = stk2[t2--];
        //for(int i = 0; i <= t1; i++) cout << stk1[i] << ' ';
    }
    //后缀表达式求值
    LL get_ans()
    {
        for(int i = 0; i <= t1; i++)
        {
            string temp = stk1[i];
            if(temp != "+"  && temp != "*")
            {
                int num = stoi(temp);
                //cout << num << endl;
                ans[++t3] = num;
            }
            else
            {
                int num1 = ans[t3--], num2 = ans[t3--];
                if(temp == "+") ans[++t3] = (LL)num2 + num1;
                else if(temp == "*") ans[++t3] = (LL)num2 * num1;
            }
        }
        return ans[t3];
    }
    
    int main()
    {
        cin >> m >> n;
        while(m--)
        {
            cin >> s >> x;
            zl.insert({s, x});
        }
        
        while(n--)
        {
            LL ans = 0;
            string str[N];
            int idx = 0;
            t1 = t2 = t3 = -1;
            cin >> s;
            //cout << s << endl;
            int l = s.length();
            //将化学式转化为带括号、加法、乘法的算术表达式
            for(int i = 0; i < l; i++)
            {
                string t = "", num = "";
                //大写字母+小写字母的情况
                if(s[i] >= 'A' && s[i] <= 'Z' && s[i + 1] >= 'a' && s[i + 1] <= 'z')
                {
                    t += s[i], t += s[++i];
                    str[idx++] = to_string(zl[t]);
                    //注意判断下一个是否越界
                    if(i + 1 < l && s[i + 1] >= '0' && s[i + 1] <= '9') str[idx++] = "*";
                    else if(i + 1 < l && s[i + 1] != ')') str[idx++] = "+";
                }
                //单个大写字母的情况
                else if(s[i] >= 'A' && s[i] <= 'Z' && !(s[i + 1] >= 'a' && s[i + 1] <= 'z'))
                {
                    t += s[i];
                    str[idx++] = to_string(zl[t]);
                    //注意判断下一个是否越界
                    if(i + 1 < l && s[i + 1] >= '0' && s[i + 1] <= '9') str[idx++] = "*";
                    else if(i + 1 < l && s[i + 1] != ')') str[idx++] = "+";
                }
                //数字的情况
                else if(s[i] >= '0' && s[i] <= '9')
                {
                    while(s[i] >= '0' && s[i] <= '9') t += s[i++];
                    i--;
                    str[idx++] = t;
                    if(i + 1 < l && s[i + 1] != ')') str[idx++] = "+";
                }
                //括号的情况
                else if(s[i] == '(') str[idx++] = "(";
                else if(s[i] == ')') 
                {
                    str[idx++] = ")";
                    if(i + 1 < l && s[i + 1] >= '0' && s[i + 1] <= '9') str[idx++] = "*";
                }
            }
            //for(int i = 0; i < idx; i++) cout << str[i] << ' ';
            //puts("");
            //将算术表达式化为后缀表达式
            to_q(str, idx);
            //puts("");
            printf("%lld
    ", get_ans());
        }
        return 0;
    }
    

    C.消减整数

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

    相关:找规律

    首先减数必须是(1、10、100、1000...),找具体的数模拟可以发现:

    如果(H = ...10010), (a(减数) = 10),减一次后(H = ...10000)

    如果(H = ...10000), (a(减数) = 10),减一次后(H = ...01110)

    很明显,如果H对应位数上为1,则必须通过减a来把1减掉;如果对应位数上为0,减掉a会在原本位置上生成一个1;由于无论如何都不会影响已经减过的位置上的数,所以如果对应位置上为0,则可以直接跳过。

    由此可得,如果H对应位数上的数为1,则必须减一次a,否则a加倍。

    #include <iostream>
    
    using namespace std;
    
    int t, h;
    
    int main()
    {
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d", &h);
            int ans = 0, a = 1, n = 1;
            while(h)
            {
                h -= a;
                ans++;
                if((h >> (n - 1)) & 1) continue;
                a <<= 1, n++;
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    D.消灭星星

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

    相关:二进制枚举、模拟

    由数据范围可以知道,字母的种类很少,所有我们可以通过二进制枚举来枚举出所有保留字母的情况,然后去每一种情况检查是否有合法方案。

    二进制位中,‘1’代表保留,‘0'代表可以消去。

    注意:题目的要求是某一种字母一个都不能被消去,而不是剩余。

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <bitset>
    
    using namespace std;
    
    const int N = 22;
    
    int t, n, m, k;
    char s[N][N];
    //将不包含保留字母的行和列全部删掉(变为'.'),最后判断是否还剩下没有消去的星星
    bool check(char ts[N][N], bitset<10> bs)
    {
        bool zm[11];
        for(int i = 0; i < 10; i++) zm[i] = bs[i];
        //删除行
        for(int i = 0; i < n; i++)
        {
            bool f = true;
            for(int j = 0; j < n; j++)
                if(ts[i][j] >= 'A' && ts[i][j] <= 'Z' && zm[ts[i][j] - 'A'])
                {
                    f = false;
                    break;
                }
            if(f) for(int j = 0; j < n; j++) ts[i][j] = '.';
        }
        //删除列
        for(int i = 0; i < n; i++)
        {
            bool f = true;
            for(int j = 0; j < n; j++)
                if(ts[j][i] >= 'A' && ts[j][i] <= 'Z' && zm[ts[j][i] - 'A'])
                {
                    f = false;
                    break;
                }
            if(f) for(int j = 0; j < n; j++) ts[j][i] = '.';
        }
        //检查是否还有剩余的星星
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                if(ts[i][j] == '*')
                    return false;
        return true;
    }
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            scanf("%d%d%d", &n, &m, &k);
            for(int i = 0; i < n; i++) scanf("%s", s[i]);
            int x = 0, y = 0;
            for(int i = 0; i < m; i++) x = (x << 1) + 1;
            for(int i = 0; i < m - k; i++) y = (y << 1) + 1;
            bool f = false;
            //二进制枚举所有要保存的字母集合,然后判断在该条件下方案是否可行
            for(int i = x; i >= y; i--)
            {
                bitset<10> bs(i);
                //cout << i << ' ' << bs.count() << endl;
                if(bs.count() < m - k) continue;
                char ts[N][N];
                memcpy(ts, s, sizeof s);
                if(check(ts, bs))
                {
                    f = true;
                    break;
                }
            }
            if(f) puts("yes");
            else puts("no");
        }
        
        return 0;
    }
    

    E.春游

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

    总共有五种情况:

    1. 全部坐二人船:((n + 1) / 2 * a)
    2. 坐二人船多出来的1人和其中一个二人船坐三人船:((n - 2) / 2 * a + b)
    3. 全部坐三人船:((n + 2) / 3 * b)
    4. 多出来的1人和其中一个三人船坐两个二人船:((n - 2)/3 * b + 2 * a)
    5. 多出来的2人坐二人船:(n / 3 * b + a)

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    int t;
    LL n, a, b;
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            LL ans1 = 0, ans2 = 0;
            scanf("%lld%lld%lld", &n, &a, &b);
            ans1 = min((n + 1) / 2 * a, (n - 2) / 2 * a + b);
            ans2 = min((n + 2) / 3 * b, min((n - 2) / 3 * b + 2 * a, n / 3 * b + a));
            printf("%lld
    ", min(ans1, ans2));
        }
        return 0;
    }
    
    

    F.五连珠

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

    模拟,用pair数组存每个数在矩阵中的位置,用st数组存行、列、对角线的未划掉的数字个数,每次划完后对st数组检查判断即可。

    AC代码:

    #include <iostream>
    #include <algorithm>
    
    #define x first
    #define y second
    
    using namespace std;
    
    typedef pair<int, int> PII;
    
    const int N = 10;
    
    int t;
    int a, b, c;
    PII pa[30], pb[30];
    int sta[12], stb[12];
    
    void work(int x, int st[], PII p[])
    {
        st[p[x].x]--, st[p[x].y + 5]--;
        if(p[x].x == p[x].y) st[10]--;
        if(p[x].x + p[x].y == 4) st[11]--;
    }
    
    bool check(int st[])
    {
        for(int i = 0; i < 12; i++)
            if(!st[i])
                return true;
        return false;
    }
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            for(int i = 0; i < 12; i++) sta[i] = stb[i] = 5;
            //录入数据,并存下各个数字在矩阵中的坐标
            for(int i = 0; i < 5; i++)
                for(int j = 0; j < 5; j++)
                {
                    scanf("%d", &a);
                    pa[a] = {i, j};
                }
            for(int i = 0; i < 5; i++)
                for(int j = 0; j < 5; j++)
                {
                    scanf("%d", &b);
                    pb[b] = {i, j};
                }
            //按顺序划掉数字,注意在得到答案后,一定要把剩余的数字读完
            bool fa = false, fb = false;
            for(int i = 0; i < 25; i++)
            {
                scanf("%d", &c);
                if(!fa && !fb)
                {
                    work(c, sta, pa);
                    work(c, stb, pb);
                    fa = check(sta), fb = check(stb);
                    //cout << fa << ' ' << fb << endl;
                    if(fa && fb) puts("0");
                    else if(fa) puts("1");
                    else if(fb) puts("2");
                }
                //if(fa || fb) break;
            }
        }
        
        return 0;
    }
    

    G.有始有终

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

    相关:BFS、最短路、Flood Fill

    一开始的思路:将可以直接通过的边的边权置为0,要变电梯的置为1,然后做一遍最短路。但是完全处理不好变电梯的情况。。。

    参考大佬们的题解后,得到的思路:

    1. 利用Flood Fill算法(就是求连通块的算法)把能直接通过的连通块求出来;
    2. 将与连通块不连通的边界全部变成电梯;
    3. 重复1、2步骤,直到终点第一次在连通块中时,前两个步骤执行的次数即为答案。

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <cmath>
    
    #define x first
    #define y second
    
    using namespace std;
    
    typedef pair<int, int> PII;
    
    const int N = 33;
    
    int t, n, m, sx, sy, px, py, d;
    int g[N][N];
    bool st[N][N];
    int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
    //flood fill求连通块
    void bfs_1(int x, int y)
    {
        queue<PII> q;
        q.push({x, y});
        st[x][y] = true;
        
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                if(st[i][j]) q.push({i, j});
        
        while(q.size())
        {
            auto t = q.front();
            q.pop();
            for(int i = 0; i < 4; i++)
            {
                int a = t.x + dx[i], b = t.y + dy[i], dd;
                if(a < 1 || a > n || b < 1 || b > m || st[a][b]) continue;
                if(abs(g[a][b] - g[t.x][t.y]) <= d || g[a][b] == -1 || g[t.x][t.y] == -1)
                {
                    st[a][b] = true;
                    q.push({a, b});
                }
            }
        }
    }
    //将边缘部分变为电梯
    void bfs_2(int x, int y)
    {
        queue<PII> q;
        q.push({x, y});
        
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                if(st[i][j]) q.push({i, j});
        
        while(q.size())
        {
            auto t = q.front();
            q.pop();
            for(int i = 0; i < 4; i++)
            {
                int a = t.x + dx[i], b = t.y + dy[i], dd;
                if(a < 1 || a > n || b < 1 || b > m || st[a][b]) continue;
                if(abs(g[a][b] - g[t.x][t.y]) > d) g[a][b] = -1;
            }
        }
    }
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            memset(st, false, sizeof st);
            scanf("%d%d%d%d%d%d%d", &m, &n, &sy, &sx, &py, &px, &d);
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= m; j++)
                    scanf("%d", &g[i][j]);
            int ans = 0;
            while(1)
            {
                bfs_1(sx, sy);
                if(st[px][py]) break;
                bfs_2(sx, sy);
                ans++;
            }
            
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    H.匹配矩阵

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

    相关:模拟

    将小矩阵旋转3次得到的矩阵全部求出来,然后对每一小矩阵,在大矩阵中求其数量,最后求和即为答案。

    AC代码:

    #include <iostream>
    
    using namespace std;
    
    const int N = 22;
    
    int t, n, m, h, w;
    char big[N][N], l1[N][N], l2[N][N], l3[N][N], l4[N][N];
    //旋转90度
    void tran1(char l1[N][N], char l2[N][N])
    {
        for(int i = 0, p = 0; p < w; i++, p++)
            for(int j = h - 1, q = 0; q < h; j--, q++)
                l2[p][q] = l1[j][i];
    }
    //旋转180度
    void tran2(char l1[N][N], char l2[N][N])
    {
        for(int i = h - 1, p = 0; p < h; i--, p++)
            for(int j = w -1, q = 0; q < w; j--, q++)
                    l2[p][q] = l1[i][j];
    }
    //旋转270度
    void tran3(char l1[N][N], char l2[N][N])
    {
        for(int i = w - 1, p = 0; p < w; i--, p++)
            for(int j = 0, q = 0; q < h; j++, q++)
                l2[p][q] = l1[j][i];
    }
    //检查矩阵是否相同
    bool check(int x, int y, char l[N][N], int h, int w)
    {
        for(int i = x, p = 0; p < h; i++, p++)
            for(int j = y, q = 0; q < w; j++, q++)
                if(l[p][q] != '#' && l[p][q] != big[i][j])
                    return false;
        return true;
    }
    //求大矩阵中小矩阵的数量
    int get_num(char l[N][N], int h, int w)
    {
        int num = 0;
        for(int i = 0; i + h - 1 < n; i++)
            for(int j = 0; j + w - 1 < m; j++)
                if(check(i, j, l, h, w)) num++;
        return num;
    }
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            scanf("%d%d", &n, &m);
            for(int i = 0; i < n; i++) scanf("%s", big[i]);
            scanf("%d%d", &h, &w);
            for(int i = 0; i < h; i++) scanf("%s", l1[i]);
            tran1(l1, l2);
            tran2(l1, l3);
            tran3(l1, l4);
            int ans = get_num(l1, h, w) + get_num(l2, w, h) + get_num(l3, h, w) + get_num(l4, w, h);
            printf("%d
    ", ans);
        }
        
        return 0;
    }
    

    I.螺旋矩阵

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

    相关:模拟

    初始化一个大矩阵(最大数据范围的矩阵),从矩阵中间的点开始模拟填入字符,按照右、上、左、下的顺序循环,填入的条件是:下一个方向的相邻的那一行已经填入的字符。只需要按照这个条件循环结束,然后按顺序输出大矩阵中非0的元素即可。

    AC代码

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int N = 80;
    
    int t;
    string s;
    char g[N][N];
    int dx[4] = {0, -1, 0, 1}, dy[4] = {1, 0, -1, 0};
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            cin >> s;
            memset(g, 0, sizeof g);
            int l = s.length();
            g[40][40] = s[0];
            for(int x = 40, y = 41, f = 1, i = 1; i < l; f = (f + 1) % 4)
            {
                while(g[x + dx[(f + 1) % 4]][y + dy[(f + 1) % 4]]) 
                {
                    g[x][y] = (i < l ? s[i++] : ' ');
                    x += dx[f];
                    y += dy[f];
                }
            }
            for(int i = 0; i < 80; i++)
            {
                bool f = false;
                for(int j = 0; j < 80; j++)
                    if(g[i][j] != 0)
                    {
                        f = true;
                        printf("%c", g[i][j]);
                    }
                if(f) putchar('
    ');
            }
            putchar('
    ');
        }
        return 0;
    }
    

    J.统计个数

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

    暴力枚举即可

    直接暴力,线的数量会被算两次,三角的数量会被算6次

    AC代码:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int N = 210;
    
    int t, n, m;
    bool g[N][N];
    
    int gcd(int a, int b)
    {
        return b ? gcd(b, a % b) : a;
    }
    
    int main()
    {
        cin >> t;
        while(t--)
        {
            memset(g, false, sizeof g);
            scanf("%d%d", &n, &m);
            for(int i = 0; i < m; i++)
            {
                int x, y;
                scanf("%d%d", &x, &y);
                g[x][y] = true;
                g[y][x] = true;
            }
            
            int p = 0, q = 0;
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                {
                    if(i == j) continue;
                    for(int k = 1; k <= n; k++)
                    {
                        if(i == k || j == k) continue;
                        if(g[j][i] && g[i][k])
                        {
                            q++;
                            if(g[j][k]) p++;
                        }
                    }
                }
                
            //cout << p << ' ' << q << endl;
            if(!p) puts("0/1");
            else
            {
                p = p / 6 * 3, q /= 2;
                int G = gcd(p, q);
                printf("%d/%d
    ", p / G, q / G);
            }
        }
        
        return 0;
    }
    
  • 相关阅读:
    asp.net中插件开发模式说明
    Url路径重写的原理
    Linux上搭建各种环境(一)
    常见Post提交数据方式接口测试
    Jmeter4.0----CSV Data Set Config_使用表格进行参数化(22)
    弱网测试----App
    性能测试基础
    使用fiddler实现手机抓包
    Jmeter4.0----发送邮箱之SMTP Sampler(21)
    Jmeter4.0----发送测试结果到邮箱之邮件观察仪(20)
  • 原文地址:https://www.cnblogs.com/grain-rain/p/14797402.html
Copyright © 2011-2022 走看看