新生赛 F,H,J 题解
F. 金苹果
搜索题。
首先在输入中记录点 \(S,P,T\) 的坐标。
第一问,搜索要求不经过点 \(P\),可以将 px,py 记录成 #,然后从 \(S\) 开始搜(dfs 或 bfs 均可)能不能到达 \(T\)。
第二问,从 \(S\) 出发能否先经过 \(P\) 再到达 \(T\),就相当于能否从 \(P\) 出发,既能到达\(S\)又能到达\(T\),两次搜索判断即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf = 50 + 7;
int n, m;
char s[inf][inf];
int dx[4] = {0, 0, 1, -1},
dy[4] = {1, -1, 0, 0};
bool vis[inf][inf];
bool dfs(int x, int y, char ed)
{
if (s[x][y] == ed)
{
return 1;
}
vis[x][y] = 1;
bool ret = 0;
for (int i = 0; i < 4; i++)
{
int xx = x + dx[i], yy = y + dy[i];
if (xx < 1 || xx > n || yy < 1 || yy > m)
continue;
if (vis[xx][yy] || s[xx][yy] == '#')
continue;
ret = ret || dfs(xx, yy, ed);
if (ret)
return ret;
}
return ret;
}
struct node
{
int x, y;
node() {}
node(int x, int y) : x(x), y(y) {}
};
void _()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> (s[i] + 1);
}
int sx, sy, px, py, tx, ty;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (s[i][j] == 'S')
sx = i, sy = j;
if (s[i][j] == 'P')
px = i, py = j;
if (s[i][j] == 'T')
tx = i, ty = j;
}
}
memset(vis, 0, sizeof(vis));
s[px][py] = '#';
cout << (dfs(sx, sy, 'T') ? "Yes" : "No") << ' ';
s[px][py] = 'P';
memset(vis, 0, sizeof(vis));
bool rchS = dfs(px, py, 'S');
memset(vis, 0, sizeof(vis));
bool rchT = dfs(px, py, 'T');
cout << ((rchS && rchT) ? "Yes" : "No") << '\n';
}
signed main()
{
int qwq = 1;
cin >> qwq;
for (int i = 1; i <= qwq; i++)
_();
return 0;
}
H. 简单点
对于两个数之间的运算,第一反应是乘法得到的结果会比加法大。但是会有一种特殊情况,即两个运算数中存在 \(1\),此时 \(a + 1 > a * 1\)。
于是我们将找出所有的 \(1\),进行加操作,找出所有 \(>1\) 的数,进行乘操作,然后两者结果相乘即可。
但是仔细观察会发现,如果只有一个 \(1\),那么这时候需要将这个 \(1\) 先和 \(>1\) 的 最小数字 相加后再相乘,因为 \(a * b * c + 1 < a * (b + 1) * c <(a + 1) * b * c\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf = 1e5 + 7, mod = 998244353;
int n, sum1, ans;
vector<int> a;
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int k;
cin >> k;
if (k == 1)
sum1++;
else
a.push_back(k);
}
sort(a.begin(), a.end());
if (sum1 == 1)
{
ans = sum1 + a[0];
for (int i = 1; i < a.size(); i++)
ans = ans * a[i] % mod;
}
else
{
ans = 1;
if (sum1)
a.push_back(sum1);
for (int i = 0; i < a.size(); i++)
ans = ans * a[i] % mod;
}
cout << ans << '\n';
return 0;
}
由于取模的特性,在乘的过程中可能重新得到 \(1\),此时应该进行乘操作,但是如果直接判断是否有 \(1\),或取 max 则会导致出错。
此 hack 来自方少
如 \(96829702242 = 97 * 998244353 + 1\),可以分解为 \(96829702242 = 2 * 3 * 7 * 13 * 13 * 19 * 19 * 23 * 31 * 53\),则当程序运行到 \(* 53 \% 998244353\) 之后得到的结果为 \(1\),此时会出错。
J. 读书人
模拟题。
洛谷原题弱化版。
本题代码(有分步解析):
#include <bits/stdc++.h>
using namespace std;
char s[55], a[55], b[55];
char w[20] = " GSBQWSBQY";
int cnt, cnta, cntb;
bool pd_xsd, pd_top0;
bool pd_all0(int n)
{
while (n > 0)
{
if (a[n--] != '0')
return 0;
}
return 1;
}
bool pd_W(int n)
{
while (n > 5)
{
if (a[--n] != '0')
return 0;
}
return 1;
}
int main()
{
cin >> s;
int len = strlen(s);
for (int i = 0; i < len; i++)
{
if (s[i] == '.')
{
pd_xsd = 1;
break;
}
}
int cnt = len - 1;
if (pd_xsd)
{
while (s[cnt--] != '.')
b[++cntb] = s[cnt + 1];
while (cnt >= 0)
a[++cnta] = s[cnt--];
}
else
{
for (int i = 0; i < len; i++)
a[len - i] = s[i];
cnta = len;
}
if (a[cnta] > '9' || a[cnta] < '0')
{
if (a[cnta] == '-')
cout << 'F';
a[cnta--] = 0;
}
while (cnta > 0 && a[cnta] == '0')
a[cnta--] = 0;
if (cnta == 0)
cout << 0;
else
{
while (cnta)
{
if (a[cnta] != '0')
{
pd_top0 = 1, cout << a[cnta];
if (cnta != 1 && cnta != 5)
cout << w[cnta];
if (4 < cnta && cnta < 9)
{
if (pd_W(cnta))
cout << 'W';
}
}
else if (pd_top0)
{
pd_top0 = 0;
if (!pd_all0(cnta))
{
cout << 0;
}
}
cnta--;
}
}
if (pd_xsd && s[len - 1] != '.')
cout << 'D';
for (int i = cntb; i > 0; i--)
cout << b[i];
return 0;
}
这题的难点主要在于整数部分的处理。
先看其他部分:
判断有无小数点:
for (int i = 0; i < len; i++)
{
if (s[i] == '.')
{
pd_xsd = 1;
break;
}
}
将整数和小数部分分开,分别用 a,b 两个数组存下来:
int cnt = len - 1;
if (pd_xsd)
{
while (s[cnt--] != '.')
b[++cntb] = s[cnt + 1];
while (cnt >= 0)
a[++cnta] = s[cnt--];
}
else
{
for (int i = 0; i < len; i++)
a[len - i] = s[i];
cnta = len;
}
判断符号位,并去除前导 0。
if (a[cnta] > '9' || a[cnta] < '0')
{
if (a[cnta] == '-')
cout << 'F';
a[cnta--] = 0;
}
while (cnta > 0 && a[cnta] == '0')
a[cnta--] = 0;
整数部分先跳过,小数部分:
if (pd_xsd && s[len - 1] != '.')
cout << 'D';
for (int i = cntb; i > 0; i--)
cout << b[i];
最后看整数部分:
if (cnta == 0)
{
cout << 0;
// 如果没有整数部分(去除前导 0 导致的),则直接输出 0。
}
else
{
while (cnta)
{ // 处理每一位
if (a[cnta] != '0')
{
pd_top0 = 1, cout << a[cnta];
// pd_top0 代表判断是否是 0 而且是连续段中的第一个 0,用于连续 0 的输出。
if (cnta != 1 && cnta != 5)
cout << w[cnta];
// 除了第一位和第五位特殊,其他情况全部可以直接输出单位。
if (4 < cnta && cnta < 9)
{ // 第 5 到 8 位的数字需要考虑是否输出 'W'。
if (pd_W(cnta))
cout << 'W';
}
}
else if (pd_top0)
{ // 一段连续的 0,只需要其第一位进行输出,其他的不输出。
pd_top0 = 0;
if (!pd_all0(cnta))
{ // 特殊情况,如果当前位之后全是 0,则不需要输出,否则需要输出。
cout << 0;
}
}
cnta--;
}
}
其中:
第 5 到 8 位的数字需要考虑是否输出 'W'
我们有代码:
bool pd_W(int n)
{
while (n > 5)
{ // 若此时第 5 位到当前位还有非 0 的数字,则不需要输出 'W'
if (a[--n] != '0')
return 0;
}
return 1;
}
特殊情况,如果当前位之后全是 0,则不需要输出,否则需要输出。
我们有代码:
bool pd_all0(int n)
{
while (n > 0)
{//判断当前位到最低位是否全是 0
if (a[n--] != '0')
return 0;
}
return 1;
}

浙公网安备 33010602011771号