SMU Winter 2023 蓝桥杯模拟赛 1 个人题解

 

目录

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] 完全二叉树的权值


A [蓝桥杯 2013 省 B] 带分数b

题意: 

 给出一个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 <bool> 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';
	}
};

 



B [蓝桥杯 2013 省 AB] 错误票据

 题意: 

 给出若干个数,其中只有一个值出现了两次,除一个值没出出现外其他值均连续

思路:

注意到n很小,读入后排序然后枚举一边即可。

此题有一个读入的问题不太好处理,

可以用while(cin>>a)或者while(scanf("%d", %a) !=EOF)

来读取到文件末尾的每一个数

代码:

	void solve() {
        vector <int> 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;

	}

 


 

 C [蓝桥杯 2013 省 B] 翻硬币

 题意: 

有两个01串,每次操作可以任意改变两个相邻的数,问最少多少次操作可以把A串变为B串。

思路:

题目保证一定有解,所以两个串的相同位置的不同情况一定出现偶数次,两两匹配算出花费即可

小问题:为什么一定是12、34、56...这样匹配? 23匹配在一起拿行不行?

代码:

	void solve() {
        string s0, s1;
        cin >> s0 >> s1;
        int n = s0.size();
        vector <int> 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';
	}

 


 

 D [蓝桥杯 2019 省 B] 灵能传输

 题意: 

 给出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<ll> 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 <ll> ve, vr;
        vector <bool> 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';
	}

 


 

 E [蓝桥杯 2019 省 B] 后缀表达式

 题意: 

给出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 <int> 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';
	}

 


   
F [蓝桥杯 2019 省 B] 等差数列​​​​​​​

 题意: 

 给出n个数,问最短的包含这n个数的等差数列有几项

思路:

 排序后枚举得出最小公差,答案即为(max-min)/最小公差+1

注意特判公差为0的情况

代码:

    void solve() {
        cin >> n;
        vector<int> 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';

	}

 


 

 G [蓝桥杯 2019 省 B] 特别数的和

 题意: 

 输出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';

	}
};

 


 

H [蓝桥杯 2019 省 AB] 完全二叉树的权值

 题意: 

给出一颗包含n个节点的完全二叉树,问哪一层的权值之和最大

思路:

枚举即可

代码:

	void solve() {
        cin >> n;
        vector <ll> 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';

	}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值