牛客小白月赛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;
}
原文地址:https://www.cnblogs.com/grain-rain/p/14797402.html