目录
A [蓝桥杯 2013 省 B] 带分数b
B [蓝桥杯 2013 省 AB] 错误票据
C [蓝桥杯 2013 省 B] 翻硬币
D [蓝桥杯 2019 省 B] 灵能传输
E [蓝桥杯 2019 省 B] 后缀表达式
F [蓝桥杯 2019 省 B] 等差数列
G [蓝桥杯 2019 省 B] 特别数的和
H [蓝桥杯 2019 省 AB] 完全二叉树的权值
给出一个n,问使得式子n=a+b/c成立
且三数满足1到9恰好在它们中出现一次
的abc的正整数取值有多少种
暴力枚举即可,首先确定a,则有b/c=n-a,即b=(n-a)*c
所以只需要枚举c即可,易知三数的数位之和随c的增大单调递增
所以当三数的数位之和大于9时即可停止枚举c
int cc(ll n) {
int res = 0;
while (n) {
res++;
n/=10;
}
return res;
}
bool check(int a, int b, int c) {
vector vis (10);
while (a) {
vis[a%10] = 1;
a/=10;
}
while (b) {
vis[b%10] = 1;
b/=10;
}
while (c) {
vis[c%10] = 1;
c/=10;
}
for (int i = 1; i < 10; ++i) {
if (!vis[i])return 0;
}
return 1;
}
struct Solver {
void solve() {
cin >> n;
ll a, b, c;
for (int i = 1; i < n; ++i) {
a = i;
b = n-i;
c = 2;
int ca = cc(a);
while (ca + cc(c*b) + cc(c) < 9) {
c++;
}
while (ca + cc(c*b) + cc(c) == 9) {
ans += check(a, c*b, c);
c++;
}
}
cout << ans << '\n';
}
};
给出若干个数,其中只有一个值出现了两次,除一个值没出出现外其他值均连续
注意到n很小,读入后排序然后枚举一边即可。
此题有一个读入的问题不太好处理,
可以用while(cin>>a)或者while(scanf("%d", %a) !=EOF)
来读取到文件末尾的每一个数
void solve() {
vector ve;
cin >> a;
while (cin >> a) {
ve.push_back(a);
}
std::sort(ve.begin(), ve.end());
for (int i = 0; i < (int)ve.size()-1; ++i) {
if (ve[i] == ve[i+1]) {
b = ve[i];
}
if (ve[i]+2 == ve[i+1]) {
a = ve[i] + 1;
}
}
cout << a << ' ' << b;
}
有两个01串,每次操作可以任意改变两个相邻的数,问最少多少次操作可以把A串变为B串。
题目保证一定有解,所以两个串的相同位置的不同情况一定出现偶数次,两两匹配算出花费即可
小问题:为什么一定是12、34、56...这样匹配? 23匹配在一起拿行不行?
void solve() {
string s0, s1;
cin >> s0 >> s1;
int n = s0.size();
vector ve;
for (int i = 0; i < n; ++i) {
if (s0[i] != s1[i]) {
ve.push_back(i);
}
}
for (int i = 0; i < ve.size(); i += 2) {
ans += ve[i+1] - ve[i];
}
cout << ans << '\n';
}
给出n个数a1~an,每次可以任意选择一个i属于[2,n-1],使得
问任意操作若干次后,最小的max{ai}是多少
上述操作等价于把swap(pre[i], pre[i-1]),即交换第i-1个和第i个前缀和
注意到此时最后一个前缀和即pre[n]无法移动,
为便于处理,我们在开头添加一个同样无法移动的pre[0]=0
那么问题可以转化为给出一个数组,任意重排列除了首尾之外的任何数
使得相邻两项的最大差值最小
容易想到排序后取相邻两项差值的最大值,但需要注意首尾两数不能移动
因此把实际上的最优解画成图后应该如下图所示:
以下代码给出了一种实现方法
void solve() {
cin >> n;
vector pre(n+1);
for (int i = 1; i <= n; i++) {
cin >> pre[i];
pre[i] += pre[i-1];
}
ll L = pre[0], R = pre.back();
if (L > R) swap(L, R);
std::sort(pre.begin(), pre.end());
int l = std::lower_bound(pre.begin(), pre.end(),L) - pre.begin();
int r = std::lower_bound(pre.begin()+l+1, pre.end(),R) - pre.begin();
vector ve, vr;
vector vis (n+1);
for (int i = l; i >= 0; i -= 2) {
ve.push_back(pre[i]);
vis[i] = 1;
}
for (int i = r; i <= n; i += 2) {
vr.push_back(pre[i]);
vis[i] = 1;
}
std::reverse(vr.begin(), vr.end());
for (int i = 0; i <= n; ++i) {
if (!vis[i]) {
ve.push_back(pre[i]);
}
}
for (ll i : vr) {
ve.push_back(i);
}
ll ans = 0;
for (int i = 0; i < n; i++) {
ans = max(ans, abs(ve[i] - ve[i+1]));
}
cout << ans << '\n';
}
给出n+m+1个数,要求用n个+和m个 - 构成一个值最大的后缀表达式
需要注意后缀表达式的定义,实质上就是任意加括号
因为-(a+b+c)等价于-a-b-c,-(a-b-c)等价于-a+b+c
所以减去一个最小的数之后,其他的数就可以随意加减了,直接加上绝对值就可以
式首的数一定是最大的数,所以最大减最小后加上剩余数的绝对值即可
注意特判全+的情况
void solve() {
cin >> n >> m;
k = n + m + 1;
vector ve (k);
ll sum = 0;
for (int i = 0; i < k; ++i) {
cin >> ve[i];
}
std::sort(ve.begin(), ve.end());
if (m == 0) {
for (int i = 0; i < k; ++i) {
sum += ve[i];
}
} else {
sum -= ve[0] - ve.back();
for (int i = 1; i < k-1; ++i) {
sum += abs(ve[i]);
}
}
cout << sum << '\n';
}
给出n个数,问最短的包含这n个数的等差数列有几项
排序后枚举得出最小公差,答案即为(max-min)/最小公差+1
注意特判公差为0的情况
void solve() {
cin >> n;
vector ve(n);
for (int i = 0; i < n; i++) {
cin >> ve[i];
}
std::sort(ve.begin(), ve.end());
a = inf;
for (int i = 0; i < n-1; ++i) {
a = min(ve[i+1] - ve[i], a);
}
if (a == 0) {
cout << n << '\n';
return;
}
cout << (ve.back() - ve[0]) / a + 1 << '\n';
}
输出1~n中所有数位中含有2、0、1、9的数的和
暴力枚举即可
bool check(int x) {
while (x) {
int now = x % 10;
if (now == 2 || now == 0 || now == 1 || now == 9 ) return 1;
x /= 10;
}
return 0;
}
struct Solver {
void solve() {
cin >> n;
for (int i = 1; i <= n; ++i) {
ans += check(i) ? i : 0;
}
cout << ans << '\n';
}
};
给出一颗包含n个节点的完全二叉树,问哪一层的权值之和最大
枚举即可
void solve() {
cin >> n;
vector ve (n), sum(n);
for (int i = 1; i <= n; ++i) {
cin >> a;
sum[__lg(i) + 1] += a;
}
int mx = 0;
for (int i = 1; i < n; ++i) {
if (sum[i] > sum[mx]) mx = i;
}
cout << mx << '\n';
}