第四届蓝桥杯A组题解
视频讲解点这里
错误票据
解题思路
因为他给我们的
n
n
n行,每行有多少个元素其实是一个不确定的量,那么我们就是需要用字符串存储
第一种方法
这里我们使用了
C
+
+
C++
C++自带的字符串流
s
t
r
i
n
g
s
t
r
e
a
m
stringstream
stringstream,然后我们直接以空格分割了字符串,这个字符流厉害在于,可以自动根据空格分隔字符串,然后我们还是可以自动实现
s
t
r
i
n
g
string
string到
i
n
t
int
int和
i
n
t
int
int到
s
t
r
i
n
g
string
string的转换
第二种办法
我们使用读到文件尾的方法, 一直读取到最后为止, 然后我们在进行一个排序输出
代码实现
第一种方法
#include <bits/stdc++.h>
using namespace std;
void solve() {
vector<int> res;
int n;
cin >> n;
string tmp = "\n";
getline(cin, tmp);
for (int i = 1; i <= n; i++) {
stringstream ss;
string s;
getline(cin, s);
ss << s;
int j;
while (ss >> j) res.push_back(j);
}
sort(res.begin(), res.end());
int okk = res[0], res1 = 0, res2 = 0;
for (int i = 1; i < res.size(); i++) {
if (res[i] != okk + 1 and res[i] != okk) res1 = okk + 1;
else if (res[i] == okk) res2 = okk;
okk = res[i];
}
cout << res1 << " " << res2 << "\n";
}
signed main() {
solve();
return 0;
}
第二种方法
#include <bits/stdc++.h>
using namespace std;
int n, idx, a[100010], res1, res2;
void solve() {
scanf("%d", &n);
while (scanf("%d", &a[idx]) != EOF) idx++;
sort(a, a + idx);
int okk = a[0];
for (int i = 1; i < idx; i++) {
if (a[i] != okk + 1 and a[i] != okk) res1 = okk + 1;
else if (a[i] == okk) res2 = okk;
okk = a[i];
}
printf("%d %d\n", res1, res2);
}
signed main() {
solve();
return 0;
}
买不到的数据
解题思路
首先因为我们的数据范围很小,题目时限很宽松,我们可以直接暴力枚举所有的情况,然后我们再找最大没有出现的
然后我们还可以采用数论的知识来解决这个问题
简单数学证明
首先这个问题应该是转换为这么一个问题,我们有两个互质的数
m
m
m和
n
n
n, 我们要求取最大不能通过
m
m
m和
n
n
n表示的正整数, 我们可以先设定
m
<
n
m < n
m<n,
n
≡
r
?
(
m
o
d
?
m
)
n \equiv r \ (mod\ m)
n≡r?(mod?m), 然后因为
m
m
m和
n
n
n互质, 那么我们可以知道
0
,
?
n
,
?
2
n
,
?
.
.
.
,
(
m
?
1
)
n
0,\,n,\, 2n,\,...,(m - 1)n
0,n,2n,...,(m?1)n对于
m
m
m的余数都不一样
所以我们对于不同的余数,可以划分成为不同的集合, 然后当我们想表示一个对
m
m
m的余数为
x
x
x的集合, 我们可以找到一个最小正整数
b
b
b 使得
b
n
≡
?
x
?
(
m
o
d
?
m
)
bn \equiv \ x\ (mod\ m)
bn≡?x?(mod?m), 那么根据这个我们可以得到我们用
n
n
n和
m
m
m可以表示的最小的正整数就是
b
n
bn
bn, 我们这里不考虑特殊解, 比如为
0
0
0的情况, 然后我们可知最小满足的前一个肯定是最大不满足的也就是说最大不满足的就是
b
n
?
m
bn - m
bn?m, 然后我们要找到最大不满足, 我们就要把
b
b
b提到最大, 所以
b
=
=
m
?
1
b == m- 1
b==m?1, 所以我们最后的答案就是
(
m
?
1
)
?
n
?
m
(m - 1) * n - m
(m?1)?n?m, 化简之后就是
n
?
m
?
n
?
m
n * m - n - m
n?m?n?m, 这里特别鸣谢我的一位朋友, 帮我理清了这个流程
参考了知乎的回答: 传送门
这个博主证明了一下这个,参考了一下这个传送门
代码实现
第一种方法
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define int long long
#define endl '\n'
const int N = 1e6 + 10;
void solve() {
int n, m;
cin >> n >> m;
bitset<N> st = 0;
for (int i = 0; i <= 1000; i++) {
for (int j = 0; j <= 1000; j++) {
st[i * n + j * m] = 1;
}
}
for (int i = n * m; i; i--)
if (st[i] == 0) {
cout << i << "\n";
return;
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
solve();
return 0;
}
第二种办法
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define int long long
#define endl '\n'
const int N = 1e6 + 10;
void solve() {
int n, m;
cin >> n >> m;
cout << n * m - n - m << "\n";
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
solve();
return 0;
}
剪格子
解题思路:
这里提前说一下,我们的这个做法可以过掉蓝桥杯的样例,但是事实上并不是很优秀,因我比如我们在中间成环,或者一个
T
T
T字型无法满足
然后抛出这两种情况, 我们这个题目就比较容易发现规律了, 我们可以从左上角的那个点开始
d
f
s
dfs
dfs, 然后如果我们的总和达到了
s
u
m
/
2
sum / 2
sum/2这里的
s
u
m
sum
sum代表的是整个二维数组的总和, 我们就可以退出, 如果整个的和是奇数就是不存在, 如果大于了也返回, 然后四个方向分别遍历回溯
代码实现:
注意先输入的是
m
m
m!!!
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define int long long
#define endl '\n'
int n, m, res = INT_MAX, sum = 0;
vector<PII> dir(4);
inline void dfs(int x, int y, int step, int tmp, vector<vector<int> > &mp,
vector<vector<bool> > &vis) {
if (tmp * 2 == sum) {
res = min(res, step);
return;
}
for (int i = 0; i < 4; i++) {
int xx = x + dir[i].first, yy = y + dir[i].second;
if (xx < 0 or xx >= n or yy < 0 or yy >= m or vis[xx][yy]) continue;
vis[xx][yy] = true;
dfs(xx, yy, step + 1, tmp + mp[xx][yy], mp, vis);
vis[xx][yy] = false;
}
}
void solve() {
cin >> m >> n;
vector<vector<int> > mp(n, vector<int>(m, 0));
vector<vector<bool> > vis(n, vector<bool>(m, false));
dir[0] = {0, 1}, dir[1] = {1, 0}, dir[2] = {0, -1}, dir[3] = {-1, 0};
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> mp[i][j];
sum += mp[i][j];
}
}
if (sum & 1) {
cout << "0\n";
return;
}
dfs(0, 0, 1, mp[0][0], mp, vis);
res == INT_MAX ? cout << "0\n" : cout << res << "\n";
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
solve();
return 0;
}
大臣的旅费
解题思路
这题我们要分清楚, 我们究竟是要拿到满分还是尽可能的骗分, 因为这个是
O
I
OI
OI赛制, 按照通过的数据点给分, 而不是
A
C
M
ACM
ACM赛制, 必须全部正确才算正确
首先如果我们要想骗分, 我们可以直接套一个
F
l
o
y
d
Floyd
Floyd算法的板子上去, 这样虽然时间复杂度和空间复杂度都很高, 但是我们可以骗分, 事实上, 我在官网测试, 得到了
75
75
75分, 四个测试点最后一个没有通过
我们正确的解法就是, 随便找一个点, 然后找最远距离的一个点, 然后在通过我们找到的最远距离的点, 再去找一次, 这次的点就是我们最终的答案
代码实现
骗分代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define int long long
#define endl '\n'
void floyd(int& n, vector<vector<int> >& G) {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
}
void solve() {
int n;
cin >> n;
vector<vector<int> > G(n + 1, vector<int>(n + 1, INT_MAX));
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
if (i == j) G[i][j] = 0;
}
}
for (int i = 1; i < n; i++) {
int p, q, d;
cin >> p >> q >> d;
G[p][q] = G[q][p] = min(d, G[p][q]);
}
floyd(n, G);
int maxx = INT_MIN;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (G[i][j] != INT_MAX) maxx = max(maxx, G[i][j]);
}
}
cout << (21 + maxx) * maxx / 2 << "\n";
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
solve();
return 0;
}
正确的代码
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define int long long
#define endl '\n'
inline void dfs(int u, int fa, int distance, vector<vector<PII> > &G,
vector<int> &dist) {
dist[u] = distance;
for (int i = 0; i < G[u].size(); i++) {
PII tmp = G[u][i];
if (tmp.first != fa) {
dfs(tmp.first, u, distance + tmp.second, G, dist);
}
}
}
void solve() {
int n;
cin >> n;
vector<vector<PII> > G(n + 1);
vector<int> dist(n + 1, 0);
for (int i = 1; i < n; i++) {
int p, q, d;
cin >> p >> q >> d;
G[p].push_back({q, d});
G[q].push_back({p, d});
}
dfs(1, -1, 0, G, dist);
int u = 1;
for (int i = 1; i <= n; i++)
if (dist[i] > dist[u]) u = i;
dfs(u, -1, 0, G, dist);
u = 1;
for (int i = 1; i <= n; i++)
if (dist[i] > dist[u]) u = i;
cout << (21 + dist[u]) * dist[u] / 2 << "\n";
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
solve();
return 0;
}
带分数
解题思路
其实我们发现他本质就是
0
?
9
0~9
0?9的一个全排列, 然后我们只需要判断我们这三个数(加号前面的一个, 加号和分号前面的一个, 分号后面的一个), 分别开始点和结束点在哪里就可以了, 所以我们直接使用C++的全排列函数即可, 然后暴力枚举所有位置
然后我们的式子原来是
a
+
b
c
=
=
n
a + \frac{b}{c} == n
a+cb?==n, 然后我们左右同时乘上
c
c
c即可得到这么一个等式
a
?
c
+
b
=
n
?
c
a * c + b = n * c
a?c+b=n?c
代码实现
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
#define int long long
#define endl '\n'
int cal(int l, int r, vector<int> &nums) {
int res = 0;
for (int i = l; i <= r; i++) {
res = res * 10 + nums[i];
}
return res;
}
void solve() {
int n;
cin >> n;
vector<int> nums(9);
for (int i = 0; i < 9; i++) nums[i] = i + 1;
int res = 0;
do {
for (int i = 0; i < 9; i++) {
for (int j = i + 1; j < 9; j++) {
int a = cal(0, i, nums);
int b = cal(i + 1, j, nums);
int c = cal(j + 1, 8, nums);
if (a * c + b == c * n) res++;
}
}
} while (next_permutation(nums.begin(), nums.end()));
cout << res << "\n";
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
solve();
return 0;
}
|