第十六届蓝桥杯C&C++大学B组

第十六届蓝桥杯C&C++大学B组

刷题笔记

第一题:移动距离

小明初始在二维平面的原点,他想前往坐标(233,666)。在移动过程中,他只能采用以下两种移动方式,并且这两种移动方式可以交替、不限次数地使用:

1.水平向右移动,即沿着x轴正方向移动一定的距离。

2.沿着一个圆心在原点(0,0)、以他当前位置到原点的距离为半径的圆的圆周移动,移动方向不限(即顺时针或逆时针移动不限)。

在这种条件下,他到达目的地最少移动多少单位距离?

你只需要输出答案四舍五入到整数的结果。

解题思路:

这道题简单,先算出半径,再算出弧长相加就可以了

代码:

cpp 复制代码
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    double x = 233, y = 666;
    double r = sqrt(x * x + y * y);
    double ans = r + abs(atan2(y, x)) * r;
    cout << round(ans) << endl;
    return 0;
}

第二题:客流量上限

一家连锁旅馆在全国拥有2025个分店,分别编号为1至2025。随着节日临近,总部决定为每家分店设定每日客流量的上限,

分别记作A1, A2, ,, A2025。这些上限并非随意分配,而是需要满足以下约束条件:

1.A1,A2,...A2025必须是1至2025的一个排列,即每个A,均是1至2025之间的整数,且所有A互不相同。

2.对于任意分店i和j(1<i,j<2025,i可等于j),它们的客流量上限A和A;的乘积不得超过ixj+2025。

这些约束旨在平衡各分店客流压力,确保服务质量和运营稳定定性。

现在,请你计算这样的分配方案究竟有多少种。由于答答案可能很大,你只需输出其对10°+7取余后的结果即可。

解题思路:

第一步:

首先,因为i可以等于j,就先令i=j。得到Ai *Ai <= i * i + 2025

所以我们可以先计算一下Ai的最大取值,看看有什么启发。

cpp 复制代码
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    for (int i = 1;i <= 2025;i ++){
    	cout<< i << "<=" << floor(sqrt(pow(i,2)+2025)) << endl;
	}
    return 0;
}

运行截图:

可以发现当i>=1013 时,Ai<=i ,所以从1013到2025每个Ai只能等于i。

第二步:

所以我们再进一步分析i<=012 的情况。

因为Ai * Aj <= i * j + 2025;所以(假设J>=1013)Ai * j <= i * j + 2025;

也就是说:Ai <= i + 2025 / j。

又因为J是任意的,所以:Ai <= i + 1;

A1 <= 1,2

A2 <= 1,2,3

A3 <= 1,2,3,4

所以可以认为当 i<=1012时,每个Ai有两种选择。

也就是一共 种。

cpp 复制代码
#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    for (int i = 1;i <= 2025;i ++){
    	cout<< i << "<=" << floor(sqrt(2*2+2025)) << endl;
	}
	cout << static_cast<long long>(pow(2, 1012)) % static_cast<long long>(pow(10, 9) + 7);
    return 0;
}

但是这个 实在是太大了,long long 都不够存,所以:

cpp 复制代码
int main()
{
//    for (int i = 1;i <= 2025;i ++){
//    	cout<< i << "<=" << floor(sqrt(2*2+2025)) << endl;
//	}
	//long long mod = static_cast<long long>(pow(10, 9) + 7);
	long long mod = 1e9 + 7;
	long long ans = 1;
	for (int i = 0;i < 1012;i++){
		ans *= 2;
		ans %= mod;
	}
	cout << ans << endl;
	
    return 0;
}

第三题:可分解的正整数

定义一种特殊的整数序列,这种序列由连续递增的整数组成,并满足以下条件:

1.序列长度至少为3。

2.序列中的数字是连续递增的整数(即相邻元素之差为1),可以包括正整数、负整数或0。

例如,[1,2,3]、[4,5,6,7]和[-1,0,1]是符合条件的序列,而1,2]长度不足)和[1,2,4](不连续)不符合要求。

现给定一组包含N个正整数的数据A1,A2,.....AN。如果某个A;能够表示为符合上述条件的连续整数序列中所有元素的和,则称A,是可分解的。

请你统计这组数据中可分解的正整数的数量。

解题思路:

刚开始确实没思路,然后我列举了一些数据,计算后发现,

1:

2:-1,0,1,2

3:-2,-1,0,1,2,3

几乎每一个数n都可以分解为[-(n-1)...0...n]

是一个很简单的题

代码:

cpp 复制代码
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;

int main()
{
	long long N;
	int ans = 0;
	cin >> N;
	for(int i = 0;i < N;i ++){
		int n;
		cin >> n;
		if(n>=2){
			ans ++;
		}
	}
	cout << ans;
    return 0;
}
/*
1:
2:-1,0,1,2
3:-2,-1,0,1,2,3
*/

第四题:产值调整

不难,直接做就行

代码:

cpp 复制代码
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;

void response(int A, int B, int C, int K)
{
    while (K > 0)
    {
        K--;
        if (A == B && B == C)
        {
            break;
        }
        else
        {
            //			A = floor(static_cast<double>(B + C)/2);
            //			B = floor(static_cast<double>(A + C)/2);
            //			C = floor(static_cast<double>(A + B)/2);
            int newA = floor(static_cast<double>(B + C) / 2);
            int newB = floor(static_cast<double>(A + C) / 2);
            int newC = floor(static_cast<double>(A + B) / 2);
            A = newA;
            B = newB;
            C = newC;
        }
    }
    cout << A << " " << B << " " << C << endl;
}

int main()
{
    int n = 0;
    cin >> n;
    int A = 0, B = 0, C = 0, K = 0;
    for (int i = 0; i < n; i++)
    {
        cin >> A >> B >> C >> K;
        response(A, B, C, K);
    }
    return 0;
}

不过这里要注意

cpp 复制代码
            //			A = floor(static_cast<double>(B + C)/2);
            //			B = floor(static_cast<double>(A + C)/2);
            //			C = floor(static_cast<double>(A + B)/2);
            int newA = floor(static_cast<double>(B + C) / 2);
            int newB = floor(static_cast<double>(A + C) / 2);
            int newC = floor(static_cast<double>(A + B) / 2);
            A = newA;
            B = newB;
            C = newC;

第五题:画展布置

解题思路:

这道题也不难,现对N排序,然后从一头依次选中M个,再用滑动窗口去更新L。

不过之后看题解发现还可以更简单:可以列举一下M=4,5,6的情况,排序之后其实可以去掉绝对值符号,然后就可以化简这个式子,最后发现只和首尾的绝对差有关。

代码1:

cpp 复制代码
#include <iostream>
#include <cmath>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;

int main()
{
	int N = 0,M = 0;
	cin >> N >> M;
	vector<int> nums(N);
	for(int i = 0;i < N;i ++){
		cin >> nums[i];
	}
	sort(nums.begin(),nums.end());
	long long ans = 0;
	
	for(int i = 0;i < M - 1;i ++){
		ans += abs(nums[i + 1] * nums[i + 1] - nums[i] * nums[i]);
	}
	long long newans =ans;
	int ij = 0;
	for (int j = M - 1;j < N - 1;j ++){
		newans += abs(nums[j + 1] * nums[j + 1] - nums[j] * nums[j]);
		newans -= abs(nums[ij + 1] * nums[ij + 1] - nums[ij] * nums[ij]);
		ans = min(ans,newans);
		ij ++;
	}
	cout << ans;
    
    return 0;
}

代码2:

cpp 复制代码
int main()
{
    int N = 0, M = 0;
    cin >> N >> M;
    vector<int> nums(N);
    for (int i = 0; i < N; i++)
    {
        cin >> nums[i];
    }
    sort(nums.begin(), nums.end());
    long long ans = LLONG_MAX;
    long long newans = ans;
    for (int j = 0; j < N - M; j++)
    {
        newans = nums[j + M] * nums[j + M] - nums[j] * nums[j];
        ans = min(ans, newans);
    }
    cout << ans;

    return 0;
}

第六题:水质检测

小明需要在一条 2×n 的河床上铺设水质检测器。在他铺设之前,河床上已经存在一些检测器。

如果两个检测器上下或者左右相邻,那么这两个检测器就是互相连通的。

连通具有传递性,即如果 A 和 B 连通,B 和 C 连通,那么 A 和 C 也连通。

现在他需要在河床上增加铺设一些检测器使得所有的检测器都互相连通。

他想知道最少需要增加铺设多少个检测器?

输入格式

输入共两行,表示一个 2×n 的河床。

每行一个长度为 n 的字符串,仅包含 #.,其中 # 表示已经存在的检测器,. 表示空白。

输出格式

输出共 1 行,一个整数表示答案。

这道题就比较难了,一开始用的贪心只通过了45%,看题解后用动态规划才全部通过。

解题思路:

代码:

cpp 复制代码
#include <iostream>
#include <cmath>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;

int main()
{
	int N = 0;
	int ans = 0;
	string s1,s2;
	cin >> s1 >> s2;
    //
    N = s1.size();
    int bed[2][N];
    for (int j = 0; j < N; j++)
    {
        bed[0][j] = (s1[j] == '#') ? 1 : 0;
    }
    for (int j = 0; j < N; j++)
    {
        bed[1][j] = (s2[j] == '#') ? 1 : 0;
    }
    //
    for (int i = 0; i < N; i++)
    {
        if (bed[0][i] == 0 && bed[1][i] == 0)
        {
            bed[0][i] = 1;
            bed[1][i] = 1;
        }
        else
        {
            break;
        }
    }
    for (int i = N - 1; i >= 0; i--)
    {
        if (bed[0][i] == 0 && bed[1][i] == 0)
        {
            bed[0][i] = 1;
            bed[1][i] = 1;
        }
        else
        {
            break;
        }
    }
    //
    int db[2][N];
    db[1][0] = (bed[1][0]) ? 0 : 1;
    db[0][0] = (bed[0][0]) ? 0 : 1;
    for(int i = 1; i < N;i ++){
    	db[0][i] = min((db[0][i - 1] + (1 - bed[0][i])),(db[1][i - 1] + (1 - bed[0][i]) + (1 - bed[1][i])));
    	db[1][i] = min((db[1][i - 1] + (1 - bed[1][i])),(db[0][i - 1] + (1 - bed[0][i]) + (1 - bed[1][i])));
	}
	ans = min(db[0][N - 1],db[1][N - 1]);
	
	cout << ans;
    return 0;
}

第七题:生产车间

小明正在改造一个生产车间的生产流水线。这个车间共有 n 台设备,构成以 1 为根结点的一棵树,结点 i 有权值 wi。

其中叶节点的权值 wi,表示每单位时间将产出 wi 单位的材料并送往父结点,

根结点的权值 wi 表示每单位时间内能打包多少单位成品,

其他结点的权值 wi 表示每单位时间最多能加工 wi 单位的材料并送往父结点。

由于当前生产线中某些结点存在产能不够的问题导致生产线无法正常运行,即存在某些结点每单位时间收到的材料超过了当前结点的加工能力上限。

小明计划删除一些结点使得所有结点都能正常运行。他想知道删除一些结点后根结点每单位时间内最多能打包多少单位的成品?

解题思路:

最难的一道题,一开始想到从上往下遍历更新树,但是没思路。

最后递归遍历并用上简化版的背包问题解法,20测试中过了9个。

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 01背包:计算nums中选若干数,和≤capacity的最大值
int maxValidSum(vector<int> &nums, int capacity)
{
    vector<bool> dp(capacity + 1, false);
    dp[0] = true; // 初始状态

    // 01背包逆序遍历,避免重复选择
    for (int num : nums)
    {
        for (int i = capacity; i >= num; i--)
        {
            if (dp[i - num])
            {
                dp[i] = true;
            }
        }
    }

    // 找最大的可达和
    for (int i = capacity; i >= 0; i--)
    {
        if (dp[i])
        {
            return i;
        }
    }
    return 0;
}

// 后序遍历计算节点node的最大有效输出
int dfs(int node, vector<vector<int>> &children, vector<int> &w)
{
    // 叶子节点:有效输出就是自身权值
    if (children[node].empty())
    {
        return w[node];
    }

    // 非叶子节点:先收集所有子节点的有效输出
    vector<int> childOutputs;
    for (int child : children[node])
    {
        int childVal = dfs(child, children, w);
        if (childVal > 0)
        {
            childOutputs.push_back(childVal);
        }
    }

    // 对所有子节点的有效输出做01背包
    return maxValidSum(childOutputs, w[node]);
}

int main()
{
    int N;
    cin >> N;
    // 树
    vector<vector<int>> children(N);
    // 权重
    vector<int> w(N);

    for (int i = 0; i < N; i++)
    {
        cin >> w[i];
    }

    for (int i = 0; i < N - 1; i++)
    {
        int a, b;
        cin >> a >> b;
        // 题目中a是父,b是子,转成0索引
        children[a - 1].push_back(b - 1);
    }

    int result = dfs(0, children, w);
    cout << result << endl;

    return 0;
}

第八题:

老王计划装修房子,于是联系了一家装修公司。该公司有一套自动报价系统,

只需用户提供 N 项装修相关费用 A1,A2,...,AN,系统便会根据这些费用生成最终的报价。

然而,当老王提交数据后,他发现这套系统的运作方式并不透明:系统只会给出一个最终报价,而不会公开任何运算过程或中间步骤。

公司对此解释称,这套系统会依据某种内部算法,在每对相邻数字之间插入 +(加法)、−(减法)或 ⊕(异或)运算符,

并按照特定优先级规则计算结果:异或运算优先级最高,其次是加减。

但由于保密性,具体的运算符组合以及中间过程都不会对外公开。

为了验证系统报价是否合理,老王决定模拟其运作方式,尝试每种可能的运算符组合,计算出所有可能出现的结果的总和。

如果最终报价明显超出这个范围,他就有理由怀疑系统存在异常或误差。只是老王年事已高,手动计算颇为吃力,便向你求助。

现在,请你帮老王算出所有可能的结果的总和。由于该总和可能很大,你只需提供其对 10e9 +7 取余后的结果即可。

解题思路:

一共有三种运算符,如果只用+ ,- 的话,最后结果肯定只是好多个首项相加,然后加上异或^

可以发现规律,只有前三个式子无法与其他式子合并。而这三个式子有共同点。

总之:

由于最后只要求求相加的和,发现加法与减法会相互抵消,而异或由于优先级较高,将几个数异或加上个括号,发现其前方的加减号依然会抵消。所以大多数情况是不用考虑的。

唯一的例外是首位,它的前方只能是加号,所以只考虑首位以及与首位关联的异或即可。

这道题思路不难,但是容易出错。

代码:

cpp 复制代码
#include <iostream>
#include <cmath>
#include <vector>
#include <climits>
#include <algorithm>
using namespace std;

const int MOD = 1000000007;

long long fastPow(long long a, long long b)
{
    if (a == 0)
    {
        return 0;
    }
    long long res = 1;
    while (b > 0)
    {
        if (b & 1)
        {
            res = res * a % MOD;
        }
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main()
{
    int N;
    cin >> N;
    int nums[N];

    for (int i = 0; i < N; i++)
    {
        cin >> nums[i];
    }

    if (N == 1)
    {
        cout << nums[0] % MOD;
        return 0;
    }

    long long ans = 0;
    long long part1 = nums[0] * (fastPow(3, N - 2) * 2) % MOD;
    long long part2 = 0;
    long long s = nums[0];
    ans += part1;

    for (int i = 1; i < N - 1; i++)
    {
        s ^= nums[i];
        part2 += s * fastPow(3, N - 2 - i) * 2 % MOD;
    }
    s ^= nums[N - 1];
    part2 = (part2 + s) % MOD;
    ans += part2;
    ans %= MOD;
     cout << ans;

    return 0;
}
相关推荐
爱吃生蚝的于勒2 小时前
【Linux】网络基础(一)
linux·运维·服务器·网络·后端·算法·架构
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- Day2】
开发语言·jvm·数据库·c++·程序人生·考研·蓝桥杯
小年糕是糕手2 小时前
【35天从0开始备战蓝桥杯 -- Day1】
jvm·数据结构·c++·程序人生·算法·职场和发展·蓝桥杯
日光倾2 小时前
【Vue.js 入门笔记】Webpack 入门
vue.js·笔记·webpack
z20348315202 小时前
17届蓝桥杯嵌入式赛道开发板外设使用教程——LED
stm32·单片机·蓝桥杯
Wect4 小时前
LeetCode 39. 组合总和:DFS回溯解法详解
前端·算法·typescript
Wect4 小时前
LeetCode 46. 全排列:深度解析+代码拆解
前端·算法·typescript
颜酱4 小时前
Dijkstra 算法:从 BFS 到带权最短路径
javascript·后端·算法