喵呜~ 解决 Codeforces 1783A - A. Make it Beautiful 的小秘密!
各位小可爱们,喵呜~!今天我们一起来玩一个有趣的小游戏,是来自 Codeforces 的 1783A 题,叫做 "A. Make it Beautiful" 呢!听起来是不是就很美好呀?嘿嘿,跟着我一起来看看这个小谜题吧!
题目大意:这是个什么小挑战呀?
首先呀,我们得知道题目在说什么,对不对?
题目里说,有一个数组 a
,它呀,如果满足某个条件,就会被叫做“丑陋的”数组(ugly array)。这个条件就是:如果数组里至少有一个元素,它恰好等于它前面所有元素的和。比如说,[6, 3, 9, 6]
就是丑陋的,因为 9
等于它前面的 6 + 3
呢!还有 [5, 5, 7]
也是丑陋的,因为第二个 5
等于它前面的 5
。
那如果一个数组不丑陋,它就是“美丽的”数组(beautiful array)啦!比如 [8, 4, 10, 14]
就很美丽,因为 8
不等于 0
(第一个元素前面没东西嘛),4
不等于 8
,10
不等于 8 + 4
,14
也不等于 8 + 4 + 10
。
我们得到一个数组 a
,它有一个特点哦:它里面的元素是非递减排序的,也就是说 a_1 <= a_2 <= ... <= a_n
,而且所有元素都大于等于 1
呢。我们的任务就是,重新排列这个数组 a
,让它变得美丽!我们不能添加或删除元素,只能改变它们的顺序。如果原数组本身就美丽,那保持不变也可以哦。
如果能做到,就打印 "YES",然后打印一个美丽的排列;如果怎么都变不成美丽的,就打印 "NO"。
喵呜~ 听起来是不是有点像在玩积木呀?我们要把积木块重新堆放,让它们不要“碰巧”等于前面所有积木的高度总和呢!
喵咪的思考过程:怎么让它变美丽呢?
这个问题呀,看起来好像有点复杂,但是只要我们仔细观察,就会发现一些小秘密呢!
首先,题目给了我们一个很重要的提示:输入的数组 a
已经是非递减排序的啦(a_1 <= a_2 <= ... <= a_n
)。而且所有元素都大于等于 1
。
我们想想看,什么情况下一个数组会变得丑陋呢? 假设我们排列好的数组是 b_1, b_2, ..., b_n
。
- 第一个元素
b_1
:它前面没有元素,所以前面元素的和是0
。因为a_i >= 1
,所以b_1
肯定大于等于1
,它不可能等于0
。所以第一个元素永远不会让数组变丑陋,喵~! - 第二个元素
b_2
:如果b_2 = b_1
,那数组就丑陋啦! - 第三个元素
b_3
:如果b_3 = b_1 + b_2
,那数组就丑陋啦! - 以此类推,如果
b_k = b_1 + b_2 + ... + b_{k-1}
,就丑陋啦!
喵呜~ 什么时候会“不可能”呢?
我们来考虑最坏的情况,就是什么时候我们无论怎么排列,都无法让数组变美丽。 如果数组里所有的元素都一模一样,比如说 [C, C, C, ..., C]
。 我们随便怎么排列,得到的数组还是 [C, C, C, ..., C]
。
- 第一个元素是
C
。前面和是0
。C != 0
(因为C >= 1
)。没问题。 - 第二个元素是
C
。前面和是C
。哎呀!C
等于C
了!这个数组就丑陋了! - 而且,无论怎么排列,第二个元素总是
C
,第一个元素也总是C
,所以b_2
总是等于b_1
。 - 所以,如果数组里所有元素都相同,那它就永远是丑陋的,无法变成美丽的!喵呜~!
我们怎么知道所有元素都相同呢?因为输入的数组是排序好的嘛,所以只要检查 a[0]
(最小的那个)是不是等于 a[n-1]
(最大的那个)就行啦!如果它们相等,就说明所有元素都相等。这种情况下,我们就得乖乖地打印 "NO" 啦。
喵呜~ 什么时候可以“变美丽”呢?
如果 a[0]
不等于 a[n-1]
,那就说明数组里有不同的元素,也就是说 a[0] < a[n-1]
。这种情况下,我们有没有办法构造一个美丽的数组呢?
来,跟着我的小爪子,我们试试看这个排列方法: 把数组里最大的那个元素 a[n-1]
放在最前面,然后把剩下的元素 a[0], a[1], ..., a[n-2]
按照它们原来的顺序(从小到大)依次放在后面。 所以,我们构造的新数组 b
就会是这样子(假设数组下标从0开始): b = [a[n-1], a[0], a[1], ..., a[n-2]]
我们来验证一下,这个排列是不是总是美丽的呢?
- 检查
b_0
(也就是a[n-1]
): 它是第一个元素,前面元素的和是0
。因为a[n-1] >= 1
,所以a[n-1]
肯定不等于0
。没问题! - 检查
b_1
(也就是a[0]
): 它前面元素的和是b_0
,也就是a[n-1]
。 我们当前处理的是a[0] < a[n-1]
的情况。所以a[0]
肯定不等于a[n-1]
。没问题! - 检查
b_k
(对于k >= 2
,也就是a[k-1]
,用 0-based index):b_k
前面元素的和是S_{k-1} = b_0 + b_1 + ... + b_{k-1}
。 根据我们的构造,S_{k-1} = a[n-1] + a[0] + a[1] + ... + a[k-2]
。 我们要比较b_k = a[k-1]
和S_{k-1}
。 因为原始数组a
是非递减排序的,所以a[k-1]
肯定小于等于a[n-1]
(因为k-1 <= n-1
嘛)。 同时,S_{k-1}
呢,它等于a[n-1]
加上a[0]
和后面的一些元素。因为a[0] >= 1
,所以S_{k-1}
肯定会比a[n-1]
严格大! 所以,我们有b_k = a[k-1] <= a[n-1] < S_{k-1}
。 喵呜~ 这样一来,b_k
永远不可能等于S_{k-1}
啦!
所以呀,这个构造方法 [a[n-1], a[0], a[1], ..., a[n-2]]
在 a[0] < a[n-1]
的时候总是能得到一个美丽的数组!太棒了,是不是很简单呀?
题解方法:喵咪的魔法步骤!
总结一下我们的魔法步骤:
- 读入数据:先读入数组的长度
n
,然后把n
个整数a_1, ..., a_n
都读进来。 - 判断特殊情况:检查一下
a[0]
和a[n-1]
。- 如果
a[0]
等于a[n-1]
(也就是说,数组里所有数字都一样),那就没办法啦,直接打印 "NO"。
- 如果
- 构造美丽数组:
- 如果
a[0]
不等于a[n-1]
,那么就说明我们可以构造一个美丽的数组! - 先打印 "YES"。
- 然后,按照我们发现的“魔法顺序”打印元素:先打印
a[n-1]
(最大的那个元素),接着打印a[0], a[1], ..., a[n-2]
(剩下的元素从小到大)。 - 记得元素之间要用空格隔开哦,最后要换行!
- 如果
就是这么简单,喵~!
C++ 代码实现:喵呜~ 看看我的代码!
#include <iostream> // 用来输入输出,比如 cin 和 cout
#include <vector> // 用来使用动态数组 std::vector
#include <numeric> // 虽然这个题解没用到,但有时候做数学运算会用到哦
#include <algorithm> // 虽然这个题解没用到,但有时候排序啊查找啊会用到呢
void solve() {
int n;
std::cin >> n; // 读入数组的长度 n
std::vector<int> a(n); // 创建一个大小为 n 的整数向量(数组)
for (int i = 0; i < n; ++i) {
std::cin >> a[i]; // 循环读入数组的每个元素
}
// 题目保证了输入数组 'a' 是非递减排序的哦。
// 一个数组如果某个元素等于它前面所有元素的和,就是丑陋的。
// 情况1:如果数组里所有元素都一样
// 因为数组是排序好的,所以只要看第一个元素和最后一个元素是不是一样就行啦
if (a[0] == a[n - 1]) {
std::cout << "NO\n"; // 喵呜~ 没办法了,打印 NO
return; // 结束这个测试用例
}
// 情况2:如果数组里有不同的元素(也就是 a[0] < a[n-1])
// 这种情况下,我们总能构造一个美丽的数组!
// 构造方法是:把最大的元素放在最前面,然后跟着剩下的小元素们
// 比如,原数组 a = [a_0, a_1, ..., a_{n-2}, a_{n-1}]
// 新数组 b = [a_{n-1}, a_0, a_1, ..., a_{n-2}]
std::cout << "YES\n"; // 喵~ 可以做到,打印 YES
// 打印我们构造的美丽数组
std::cout << a[n - 1]; // 先打印最大的那个元素
for (int i = 0; i < n - 1; ++i) {
std::cout << " " << a[i]; // 然后依次打印剩下的元素,前面加个空格哦
}
std::cout << "\n"; // 最后记得换行!
}
int main() {
// 这两行是用来加速输入输出的,平时写竞赛题都会加上呢,喵~
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
int t;
std::cin >> t; // 读入测试用例的数量 t
while (t--) { // 循环处理每个测试用例
solve(); // 调用 solve 函数来解决当前的测试用例
}
return 0; // 程序完美结束!
}
这段代码是不是清晰又简洁呢?喵呜~ 就像我的小爪子一样灵活!
和题目有关的知识点:喵喵小课堂!
这个问题主要考察的是一些基础的数学观察能力和构造性算法。
- 贪心思想(Greedy Approach):虽然我们没有明确说这是贪心,但我们选择把最大的元素放在最前面,就是一种尝试“最大化”后面前缀和,从而让后续元素难以等于前缀和的策略。这有点像贪心算法的局部最优选择。
- 前缀和(Prefix Sum):题目中“前面所有元素的和”本质上就是前缀和的概念。虽然我们没有显式计算整个前缀和数组,但理解这个概念有助于我们分析为什么
b_k
不等于S_{k-1}
。 - 排序数组的性质:题目明确说了输入数组是排序的,这是解决问题的关键!它让我们可以非常容易地判断所有元素是否相同 (
a[0] == a[n-1]
),并且在构造新数组时,利用a[i] <= a[j]
(如果i < j
) 这个性质来证明我们的构造是正确的。 - 特判(Edge Cases):处理所有元素都相同的情况,这是一种常见的特判。在算法设计中,识别并单独处理这些特殊情况通常能简化问题。
- 构造性问题:这类问题要求我们“构造”一个满足条件的解,而不是仅仅判断是否存在或计算某个值。通常需要找到一个简单而通用的构造方法,并证明其正确性。
喵呜~ 希望这篇题解能帮助小可爱们更好地理解这道题呢!下次我们再一起玩别的编程游戏吧!拜拜~!