第一题
小美手机上种果树,只要成熟了就可以得到免费的水果了。
小美每天可以给果树浇水,果树成长值为下x,同时可一个给果树施肥,两次施肥至少间隔2天,果树的成长值加y,果树的成长值达到z就成熟了
小红想知道,最少需要多少天就可以领到免费的水果
输入描述:
一行三个整数x, y, z,分别表示浇水的成长值,果树成熟的成长值。
1≤x,y,z≤10^9
输出描述
一行一个整数,表示最少需要的多少天可以领到免费的水果。
示例1
输入:
1 2 10
输出:
6
第一天施肥浇水,成长值为3。
第二天浇水。或长值为3+1=4。
第三天浇水。成长值为4+1=5.
第四天施肥浇水,成长值为5+3= 8。
第五天浇水。或长值为8+1= 9。
第六天浇水。成长值为9+1= 10。
果树成熟了,可以须到免费水果了!
公式
#include <iostream>
int main() {
int value1, value2, value3;
std::cin >> value1 >> value2 >> value3;
int date = 0;
while (date <= value3) {
if (date * value1 + date / 3 * value2 == value3) {
std::cout << date << std::endl;
break;
}
date++;
}
std::cout << date << endl;
return 0;
}
第二题
大家一起吃饭的时候,总是小红先付钱,然后大家再把钱转给小红。
现在小红有n张账单,每张账单记录了有k个人一起吃饭,以及吃饭的消费 c,现在小红需要计算每个人需要转给小红多少钱。
由于大家都比较喜欢整数,所以大家每张账单会转给小红[c/k],[x] 表示对 x进行上取整。
输入描述:
第一行输入两个数 n,m(1≤n, m ≤ 10^5)表示账单数和除小红外的总人数(分别用1到 m 示)
换下来 2 乘 n 行,每 2 行表示一张账单,对于每张账单:
第一行输入两个整数k(2≤k≤m+1),c(1≤c≤10^9)表示一起吃的人数,花费。
第二行输入k一1个整数,表示除小红外有哪些人一起吃饭。
输出描述:输出 m个整数,表示每个人要给小红转账的总金额
示例1
输入
2 3
3 10
1 2
4 8
1 2 3
输出: 6 6 2
说明
第一张账单:第1、2个人都会给小红转4元
第二张账单: 第1、2、3个人都会给小红转2元
因此答案为4+2=6,4+2=6,2
#include <iostream>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
struct Data {
int b;
int c;
};
int main() {
int n, m;
cin >> n >> m;
map<int, int> personToIndex; // 用于存储人员与索引的对应关系
vector<int> amounts(m, 0); // 初始化每个人的总金额为0,索引从1开始
vector<int,vector<int>> all_person(n,vector<int>(m,0));
for (int i = 1; i <= m; ++i) {
personToIndex[i] = i;
}
vector<Data> dataArr(n);
for (int i = 0; i < n; ++i) {
int k, c;
cin >> k >> c;
dataArr[i].b = k;
dataArr[i].c = c;
vector<int> person(k-1);
for (int j = 0; j < k-1; j++) {
cin >> person[j];
int a = person[j];
all_person[i][a-1] = 1;
}
}
for (int i = 0; i < n; ++i) {
int perPersonAmount = dataArr[i].c / dataArr[i].b; // 每个人应该给小红转的金额
int s = dataArr[i].c % dataArr[i].b;
if(s){
perPersonAmount++;
}
for (int j = 0; j < m; j++) {
if(all_person[i][j] == 1){
amounts[j] += perPersonAmount; // 累加每个人需要转给小红的金额
}
}
}
// 输出每个人需要给小红转的总金额
for (int i = 0; i <= m; ++i) {
cout << amounts[i] << " ";
}
return 0;
}
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
int main(){
int n, m;
cin >> n >> m;
vector<long long>total_payment(m + 1, 0);
for (int i = 0; i < n; i++) {
int k, c;
cin >> k >> c;
total_payment_per_person = ceil(c * 1.0 / k);
total_payment[0] += c - payment_per_person;
for(int j = 0; j < k - 1; j++){
int persion;
cin >> persion;
total_payment[persion] += payment_per_person;
}
}
for (int i = 1; i <= m; i++){
cout << total_payment[i] << " ";
}
return 0;
}
第三题
小美有两个长度为m的数组a和b
小美想知道,能不能通过重排a数组使得对于任意1≤i≤n,1≤a_i+b_i≤m将会有q次询问
输入描述:
第一行一个整数q(1≤q≤30),表示询问次数
对于每一个询问:
第一行输入两个整数n,m(1≤n,m≤500)
第二行输入n个正整数a_i(-500≤a_i≤500)
第三行输入n和正整数b_i(-500≤b_i≤500)
输出描述:
q行,每一行输出一个字符串,如果能通过重排满足条件就输出“Yes”,否则输出“No”
示例1:
输入
2
5 3
-1 -2 3 4 5
-1 3 4 2 5
5 6
-1 -2 3 4 5
-1 3 4 2 5
输出
No
Yes
说明
对于第一个用例,无论怎么排列都不满足条件
对于第二个用例,将数组a重排为[5,3,-2,4,-1]时满足条件。
这个问题的思路是这样的:
- 首先,我们需要对a数组进行排序,使得它按照升序排列。这样,我们就可以保证a数组中最小的元素在最前面,最大的元素在最后面。
- 然后,我们需要对b数组进行排序,但是按照降序排列。这样,我们就可以保证b数组中最大的元素在最前面,最小的元素在最后面。
- 最后,我们需要遍历两个数组,同时比较它们对应位置的元素之和。如果对于任意1≤i≤n,1≤a_i+b_i≤m都成立,那么我们就可以通过重排a数组满足条件。否则,我们就不能满足条件。
- 这个思路的原理是,如果我们想让a_i+b_i尽可能接近m,那么我们就需要让a_i尽可能大,b_i尽可能小。反之,如果我们想让a_i+b_i尽可能接近1,那么我们就需要让a_i尽可能小,b_i尽可能大。所以,通过排序两个数组,我们就可以实现这个目的。
- 这个思路的时间复杂度是O(nlogn),因为我们需要对两个数组进行排序。空间复杂度是O(1),因为我们不需要额外的空间。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 判断是否能通过重排a数组使得对于任意1≤i≤n,1≤a_i+b_i≤m
bool can_rearrange(vector<int>& a, vector<int>& b, int m) {
// 对a数组排序
sort(a.begin(), a.end());
// 对b数组排序,但是按照降序
sort(b.begin(), b.end(), greater<int>());
// 遍历两个数组,检查是否满足条件
for (int i = 0; i < a.size(); i++) {
// 如果a_i+b_i不在[1,m]范围内,返回false
if (a[i] + b[i] < 1 || a[i] + b[i] > m) {
return false;
}
}
// 如果都满足条件,返回true
return true;
}
int main() {
// 输入询问次数
int q;
cin >> q;
// 对于每一个询问
while (q--) {
// 输入n和m
int n, m;
cin >> n >> m;
// 创建两个长度为n的数组a和b
vector<int> a(n), b(n);
// 输入a数组的元素
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 输入b数组的元素
for (int i = 0; i < n; i++) {
cin >> b[i];
}
// 调用函数判断是否能重排
bool ans = can_rearrange(a, b, m);
// 输出结果
if (ans) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
}
return 0;
}
第四题
给定n个正整数组成的数组,求平均数正好等于k的最长连续子数组的长度
输入描述:
第一行输入两个正整数n和k;
第二行输入n个正整数a_i,来表示数组。
1 ≤ n ≤ 200000
1 ≤ k_i, a_i ≤ 10^9
输出描述
如果不存在任何一个连续子数组的平均数等于k,则输出-1。
否则输出平均数正好等于k的最长连续子数组的长度。
示例1
输入
5 2
1 3 2 4 1
输出
3
滑动窗口的思路是这样的:
- 我们维护一个窗口,即一个连续的子数组,用两个变量left和right表示窗口的左右边界。
- 我们不断地移动窗口的边界,使得窗口内的元素满足一定的条件,比如平均数等于k。
- 我们用一个变量sum记录窗口内元素的和,这样我们可以在O(1)的时间内计算窗口内元素的平均数。
- 我们从左到右遍历数组,每次将right指向的元素加入窗口,然后判断是否满足条件。
- 如果满足条件,我们就更新结果为当前窗口的长度,并且尝试缩小窗口,看是否还能满足条件。
- 如果不满足条件,我们就扩大窗口,直到满足条件或者right到达数组的末尾。
- 这样,我们就可以找到所有满足条件的连续子数组,并且取其中最长的一个作为答案。
#include <iostream>
#include <vector>
using namespace std;
int maxSubarrayLengthWithAvgK(vector<int>& nums, int k) {
int n = nums.size();
int sum = 0, maxLength = -1;
int left = 0, right = 0;
// 使用双指针 left 和 right 维护一个滑动窗口
while (right < n) {
sum += nums[right]; // 将右指针对应的元素加入窗口总和
// 当窗口总和大于期望平均值乘以窗口长度时,缩小窗口
while (sum > k * (right - left + 1)) {
sum -= nums[left]; // 将左指针对应的元素从窗口总和中减去
left++; // 左指针右移,缩小窗口
}
// 如果窗口总和等于期望平均值乘以窗口长度,更新最长子数组长度
if (sum == k * (right - left + 1)) {
maxLength = max(maxLength, right - left + 1);
}
right++; // 右指针右移,扩大窗口
}
return maxLength;
}
int main() {
int n, k;
cin >> n >> k;
vector<int> nums(n);
for (int i = 0; i < n; ++i) {
cin >> nums[i];
}
int result = maxSubarrayLengthWithAvgK(nums, k);
cout << result << endl;
return 0;
}
回溯思路,找出所有符号要求的连续子序列,求最大子序列长度
#include <iostream>
#include <vector>
using namespace std;
// 全局变量,存储结果
vector<vector<int>> ans;
// 回溯函数,找出所有连续子数组
void backtrack(vector<int>& a, int k, int start, int sum, vector<int>& path) {
// 如果路径的长度大于0,判断是否满足条件
if (path.size() > 0) {
// 如果平均数等于k,将路径加入结果
if (sum == k * path.size()) {
ans.push_back(path);
}
}
// 如果开始位置超过数组的长度,返回
if (start >= a.size()) {
return;
}
// 只考虑从开始位置开始的连续子数组,不需要遍历整个数组
// 将当前元素加入路径
path.push_back(a[start]);
// 更新路径的和
sum += a[start];
// 递归调用回溯函数,从下一个位置开始
backtrack(a, k, start + 1, sum, path);
// 回溯,将当前元素移出路径
path.pop_back();
// 更新路径的和
sum -= a[start];
}
// 求平均数正好等于k的所有连续子数组
vector<vector<int>> subarrays_with_avg(vector<int>& a, int k) {
// 清空结果
ans.clear();
// 创建一个临时变量,存储当前路径
vector<int> path;
// 遍历数组,从每个位置开始调用回溯函数,初始和为0
for (int i = 0; i < a.size(); i++) {
backtrack(a, k, i, 0, path);
}
// 返回结果
return ans;
}
// 找出结果中最长的数组的值
int longest_subarray_value(vector<vector<int>>& ans) {
// 初始化结果为-1
int res = -1;
// 初始化最长的数组的长度为0
int max_len = 0;
// 遍历结果中的每个数组
for (auto& sub : ans) {
if (sub.size() > max_len) {
res = sub.size();
max_len = sub.size();
}
}
// 返回结果
return res;
}
int main() {
// 输入n和k
int n, k;
cin >> n >> k;
// 创建一个长度为n的数组a
vector<int> a(n);
// 输入a数组的元素
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// 调用函数求解所有连续子数组
vector<vector<int>> ans = subarrays_with_avg(a, k);
// 调用函数求解最长子数组的值
int res = longest_subarray_value(ans);
// 输出结果
cout << res << endl;
return 0;
}
第五题
小美有一个长度为n的数组,她最多可以进行k次操作,每次操作如下:
- 选择两个整数i, j(1 ≤ i < j ≤ n)
- 选择两个整数x, y 使得x * y = a_i * a_j
- 将a_i替换为x,将a_i替换为y
她希望最多进行k次操作后,最后数组中的元素总和尽可能大。
输入描述
一行两个整数 n, k, 表述数组长度和操作的次数
一行n个整数a_1、a_2……a_n,表示数组的元素。
1 ≤ k < n ≤ 10^5
1 ≤ a_i ≤ 10^5
输出描述
输出一个整数,表示最后数组中的元素做那个和最大值,结果对10^9+7取模
示例1
输入
5 2
1 2 3 4 5
输出
65
说明
第一次操作后,数组变为[1, 2, 12, 1, 5]
第二次操作,数组变为[1, 2, 60, 1, 1]
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int mod = 1e9 + 7;
int main(){
int n, k;
cin >> n >> k;
vector<int>nums(n);
for(int i = 0; i < n; i++){
cin >> nums[i];
}
sort(nums.begin(), nums.end(), greater<int>());
int res = 1;
for(int i = 0; i < n; i++){
if(i <= k){
res = res * nums[i];
}
else{
res += nums[i];
}
}
cout << res + k << endl;
return 0;
}