坚持每日Codeforces三题挑战:Day 5 - 题目详解(2025-06-09,难度:1300, 1500, 1700)

每天坚持写三道题第五天:

Problem - C - Codeforces 1300

Problem - E - Codeforces 1500

Problem - D - Codeforces 1700

目录

题目一:

题目大意:

解题思路:

代码(C++):

题目二:

题目大意:

解题思路:

代码(C++):

题目三:

题目大意:

解题思路:

代码(C++):


题目一:

Problem - C - Codeforces

题目大意:

让你构造一个长度为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";
}

题目二:

Problem - E - Codeforces

题目大意:

给你一个长度为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";
}

题目三:

Problem - D - Codeforces

题目大意:

现在有一个长度为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";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值