牛客小白月赛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\n", 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\n", 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
总共有五种情况:
- 全部坐二人船:\((n + 1) / 2 * a\)
- 坐二人船多出来的1人和其中一个二人船坐三人船:\((n - 2) / 2 * a + b\)
- 全部坐三人船:\((n + 2) / 3 * b\)
- 多出来的1人和其中一个三人船坐两个二人船:\((n - 2)/3 * b + 2 * a\)
- 多出来的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\n", 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,然后做一遍最短路。但是完全处理不好变电梯的情况。。。
参考大佬们的题解后,得到的思路:
- 利用Flood Fill算法(就是求连通块的算法)把能直接通过的连通块求出来;
- 将与连通块不连通的边界全部变成电梯;
- 重复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\n", 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\n", 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('\n');
}
putchar('\n');
}
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\n", p / G, q / G);
}
}
return 0;
}