最初の質問
Xiaomeiの携帯電話に果樹を植えると、熟していれば無料で果物を入手できます。
シャオメイは果樹に毎日水をやることができ、果樹の成長値は x です。同時に果樹に 1 本ずつ肥料を与えることができます。2 回の施肥の間隔は少なくとも 2 日です。成長値は果樹の成長値が y 増加し、成長値が z に達すると果樹は成熟します。
Xiaohong さんは、無料のフルーツを受け取るまでに少なくとも何日かかるかを知りたいと考えています
入力の説明:
3 つの整数 x、y、z の行。それぞれ、水やりの成長値と果樹の成熟度の成長値を表します。
1≤x,y,z≤10^9
出力の説明
1 行に 1 つの整数。無料のフルーツを受け取るまでに必要な最小日数を示します。
例 1
入力:
1 2 10
出力:
6
初日は肥料と水を与えると成長値は3になります。
翌日水やりをします。または、long 値 3+1=4。
3日目に水やり。成長値は4+1=5、
4日目に肥料と水やりをすると、成長値は5+3=8となります。
5日目に水やり。または、8+1=9 の Long 値。
6日目に水を与えます。成長値は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;
}
2番目の質問
みんなで食事をするときは、必ずシャオホンが先に支払い、その後全員がシャオホンにお金を振り込みます。
現在、Xiaohong には n 枚の請求書があり、各請求書には k 人が一緒に食事をし、その食事の消費量が記録されています。Xiaohong は、各人が Xiaohong に送金する必要がある金額を計算する必要があります。
誰もが整数を好むため、各請求書は Xiaohong [c/k] に転送されます。[x] は x が切り上げられることを意味します。
入力内容:
1行目に紙幣の枚数と小型赤外線を除く総人数(それぞれ1~mで表します)を表す2つの数字nとm(1≦n、m≦10^5)を入力します。
2 行を n 行に置き換えます。各 2 行は請求書ごとに請求書を表します。
最初の行には、一緒に食事をする人の数と費用を示す 2 つの整数 k (2≤k≤m+1)、c (1≤c≤10^9) を入力します。
2 行目には、Xiaohong 以外の誰が一緒に食事をするかを示す k-1 の整数を入力します。
出力の説明: 各人が Xiaohong に送金したい合計金額を示す m 個の整数を出力します。
例 1
入力
2 3
3 10
1
2 4 8
1 2 3
出力: 6 6 2
説明
最初の請求書: 1 人目と 2 人目が小紅に 4 元を送金します。
2 つ目の請求書: 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;
}
3番目の質問
Xiaomeiには長さmの2つの配列aとbがあります
Xiaomei は、1≤i≤n、1≤a_i+b_i≤m に対して q 個のクエリが存在するように a 配列を再配置できるかどうかを知りたいと考えています。
入力説明:
最初の行に整数 q (1≤q≤30) を入力し、問い合わせの数を示します。
各問い合わせについて:
最初の行に 2 つの整数 n、m (1≤n、m≤500) を入力し
、行に n を入力します。 2 行目 正の整数 a_i (-500≤a_i≤500) の
入力 n と 3 行目の正の整数 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
出力
いいえ
はい
説明
1 番目のユースケースでは、どのように配置しても条件が満たされませんが、
2 番目のユースケースでは、配列 a を [5,3,-2,4,-1] に並べ替えると条件が満たされます。
質問の考え方は次のとおりです。
- まず、配列を昇順にソートする必要があります。このようにして、配列内の最小の要素が先頭にあり、最大の要素が末尾にあることを確認できます。
- 次に、配列 b を降順に並べ替える必要があります。このようにして、配列 b の最大の要素が先頭にあり、最小の要素が末尾にあることを確認できます。
- 最後に、2 つの配列を走査し、対応する位置にある要素の合計を同時に比較する必要があります。いずれかの 1≤i≤n、1≤a_i+b_i≤m が true の場合、配列 a を再配置することで条件を満たすことができます。そうしないと条件を満たすことができません。
- この考え方の原理は、a_i+b_i をできるだけ m に近づけたい場合は、a_i をできるだけ大きくし、b_i をできるだけ小さくする必要があるということです。逆に、a_i+b_i をできるだけ 1 に近づけたい場合は、a_i をできるだけ小さくし、b_i をできるだけ大きくする必要があります。したがって、2 つの配列をソートすることでこれを実現できます。
- 2 つの配列をソートする必要があるため、このアイデアの時間計算量は 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;
}
4番目の質問
n 個の正の整数の配列を指定して、平均が k に正確に等しい、最も長い連続部分配列の長さを見つけます。
入力の説明:
1 行目に 2 つの正の整数 n と k を入力し、
2 行目に配列を表す 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
スライディングウィンドウのアイデアは次のとおりです。
- 連続部分配列であるウィンドウを維持し、左右の 2 つの変数を使用してウィンドウの左右の境界を表します。
- ウィンドウ内の要素が特定の条件 (平均が k に等しいなど) を満たすように、ウィンドウの境界を移動し続けます。
- 変数 sum を使用してウィンドウ内の要素の合計を記録し、O(1) 時間でウィンドウ内の要素の平均数を計算できます。
- 配列を左から右に走査し、そのたびに右で指定された要素をウィンドウに追加し、条件が満たされるかどうかを判断します。
- 条件が満たされる場合は、結果が現在のウィンドウの長さに更新され、ウィンドウを縮小して条件がまだ満たされるかどうかを確認します。
- 条件が満たされない場合は、条件が満たされるか配列の末尾に到達するまでウィンドウを拡張します。
- このようにして、条件を満たすすべての連続した部分配列を見つけて、最も長いものを答えとして採用します。
#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;
}
5番目の質問
Xiaomei は長さ n の配列を持っており、最大 k 個の操作を実行できます。各操作は次のとおりです。
- 2 つの整数 i、j (1 ≤ i < j ≤ n) を選択します。
- x * y = a_i * a_j となる 2 つの整数 x、y を選択します。
- a_i を x に、a_i を y に置き換えます
彼女は、最大 k 個の操作を実行した後、最終的な配列の要素の合計ができるだけ大きくなるようにしたいと考えています。
入力の説明
配列の長さと演算数を表す 2 つの整数 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 回目の演算後、配列は [1, 2, 12, 1, 5] になります。 2 回目の演算後、
配列は [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;
}