每天坚持写三道题第五天:
目录
题目一:
题目大意:
让你构造一个长度为n数组,这个数组满足:
1.对于这个数组中的数字来说,这个数字必须出现两次(包含两次)以上。
2.对于任意两个相同数字的下标i, j都必须满足abs(i - j) 是一个完全平方数
解题思路:
完全平方数指的是开方后的结果还是整数,比如4, 9, 16,25等
首先要注意到一个关键的点是:1也是一个完全平方数
那么对于一个数组,我们可以这么构造了:
1 1 2 2 3 3 4 4...
也就是偶数长度的数组就可以这么构造
那么奇数长度怎么构造呢?
奇数长度的话,肯定有一个数字出现了奇数次,我们这里可以让这个数字num出现3次,其余数字出现两次,方便之后的构造。
我们假设这个数字num出现的三个下标分别。为i, j, k,并且满足 i < j < k。
那么根据题目条件,我们可以得出j - i、k - j、k - i都为完全平方数。
我们还可以注意到,k - i = (k - j) + (j - i)。
也就是说,我们需要找到三个完全平方数x, y, z满足,z = x + y。
我们很容易知道,z的最小值为25,也就是此时x和y分别为9和16。
那么我们可以构造成:
1 (8个数字)1 (15个数字) 1
然后我们可以发现,8个数字这个部分,可以偶数区间的方式构造。
那15个数字的这个区间部分呢?
我们可以在15个数字中加一个数字,就可以使其变成偶数了。
也就是可以这么构造:
1 (8个数字)1 2 (14个数字)1 2
上面一共是27个数字,也就是长度小于27的数字情况是不存在的。
然后大于27的部分,我们就可以偶数构造的方式。
代码(C++):
void solve() {
int n;
std::cin >> n;
//当n为偶数的时候,直接构造成1,1,2,2,3,3
if (n % 2 == 0) {
for (int i = 1; i <= n; i++) {
std::cout << (i + 1) / 2 << " ";
}
std::cout << "\n";
return;
}
//当n小于27的时候输出-1
if (n < 27) {
std::cout << "-1\n";
return;
}
//n大于27的时候
//前27个,我们用数组来实现
int a[27];
a[0] = a[9] = a[25] = 1;
a[10] = a[26] = 2;
int x = 3;
for (int i = 1; i < 8; i += 2) {
a[i] = x;
a[i + 1] = x;
x++;
}
for (int i = 11; i < 25; i += 2) {
a[i] = x;
a[i + 1] = x;
x++;
}
for (int v : a) {
std::cout << v << " ";
}
//大于27的部分,我们直接根据偶数构造的方案构造即可
for (int i = 27; i < n; i++) {
std::cout << (i + 1) / 2 << " ";
}
std::cout << "\n";
}
题目二:
题目大意:
给你一个长度为n(其中n保证是偶数)的数组a,再给你一个数字k。
你现在需要把数组分成n / 2个部分,每个部分都是包含两个数x, y,每个部分的价值是
(x + y) / k (向下取整)。
现在你需要找到一个分配方案,n / 2个部分的价值之和最大。
解题思路:
两个数字x, y相加然后除k向下取整,(x + y) / k,我们从下面这个角度这么想这个式子:
x中包含的k的个数 (x / k),加上y中包含的k的个数 (y / k),加上x % k 和 y % k。
也就是说,不管数组如何进行划分,最后的答案肯定是需要加上每个数字包含k的个数 (a[i] / k)。
然后剩下我们就只需要关注a[i] % k后的数组了。
我们可以对每一个a[i] = a[i] % k。
现在问题变成了:
对于这个新的数组,我们现在每次取两个数字,目的是保证两个数字的大于k,要让这种组合尽可能多。
贪心的想:用大的数字和小的数字组合即可保证最多。
代码(C++):
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
i64 ans = 0;
for (int i = 0; i < n; i++) {
//计算每一个a[i]包含k的个数
ans += a[i] / k;
a[i] %= k;
}
//我们现在每次取两个数字,目的是保证两个数字的大于k,要让这种组合尽可能多
//用大的数字和小的数字组合即可保证最多
//对此时的a进行从大到小进行排序,最大的在前面。
std::sort(a.begin(), a.end(), std::greater<int>());
int l = 0, r = n - 1;
while (l < r) {
while (a[l] + a[r] < k && l < r) {
r--;
}
if (l == r) {
break;
}
ans++;
r--;
l++;
}
std::cout << ans << "\n";
}
题目三:
题目大意:
现在有一个长度为n的数组a,你在某个位置i的时候,你可以进行下面的两个操作:
1.如果数组前面,也就是[0, i - 1]的区间中有比a[i]大的数字,那么可以你可以跳到这个数字上去。
2.如果数组后面,也就是[i + 1, n - 1]的区间中有比a[i]小的数字,那么你可以跳到这个数字上面去。
现在你需要求出一个长度为n的数组ans
ans[i]表示你在a[i]的初始位置可以到达的数字中的最大值。
输出这个数组。
比如:数组2 3 1 4
你需要输出3 3 3 4
解释:在i = 0处,你可以先跳到i = 2,因为a[2] < a[0], 2 > 0。
然后你在i = 2处,可以跳跃到i = 1处,因为a[1] > a[2], 1 < 2。
解题思路:
其实这题比较显而易见的,结论比较简单的,非要说这题应该没有1700分的难度的。
可以观察样例:
2 4 1 6 3 8 5 7
输出:
8 8 8 8 8 8 8 8
代码(C++):
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
//pref[i]表示a[0]到a[i - 1]的最大值
std::vector<int> pref(n);
pref[0] = a[0];
for (int i = 1; i < n; i++) {
pref[i] = std::max(pref[i - 1], a[i]);
}
//suf[i]表示a[i]到a[n - 1]的最小值
std::vector<int> suf(n);
suf[n - 1] = a[n - 1];
for (int i = n - 2; i >= 0; i--) {
suf[i] = std::min(suf[i + 1], a[i]);
}
std::vector<int> ans(n);
ans[n - 1] = pref[n - 1];
for (int i = n - 2; i >= 0; i--) {
if (pref[i] > suf[i + 1]) {
ans[i] = ans[i + 1];
} else {
ans[i] = pref[i];
}
}
for (int v : ans) {
std::cout << v << " ";
}
std::cout << "\n";
}