Rudolf 有一个由 n 个整数组成的数组 a ,元素的编号从 1 到 n 。
在一次操作中,他可以选择索引 i ( 2≤i≤n−1 ) 并赋值:
- a i − 1 = a i − 1 − 1 a_{i−1} = a_{i−1} −1 ai−1=ai−1−1
- a i = a i − 2 a_i = a_i−2 ai=ai−2
- a i + 1 = a i + 1 − 1 a_{i+1} = a_{i+1}−1 ai+1=ai+1−1
鲁道夫可以任意多次使用这个运算。任何索引 i i i 都可以使用 0 0 0 次或更多次。
他能用这个运算使数组中的所有元素都等于零吗?
输入
输入的第一行包含一个整数
t
(
1
≤
t
≤
1
0
4
)
t ( 1≤t≤10^4 )
t(1≤t≤104) - 测试中的测试用例数。
每个案例的第一行包含一个整数 n ( 3 ≤ n ≤ 2 ⋅ 1 0 5 ) n ( 3≤n≤2⋅10^5 ) n(3≤n≤2⋅105)- 数组中的元素个数。
每个案例的第二行包含 n n n 个整数 a 1 , a 2 , … , a n ( 0 ≤ a j ≤ 1 0 9 ) a_1,a_2,…,a_n ( 0≤a_j≤10^9 ) a1,a2,…,an(0≤aj≤109) - 数组中的元素
保证所有测试用例中 n n n 的值之和不超过 2 ⋅ 1 0 5 2⋅10^5 2⋅105 。 输出
对于每个测试用例,如果可以通过所述操作使数组的所有元素为零,则输出 “YES”。否则,输出 “NO”。
每个字母可以以任何大小写(小写或大写)输出。例如,字符串 “yEs”、“yes”、"Yes "和 "YES "将被视为肯定答案。
Example
input
7
5
1 3 5 5 2
5
2 4 4 5 1
5
0 1 3 3 1
6
5 6 0 2 3 0
4
1 2 7 2
3
7 1 0
4
1 1 1 1
output
YES
NO
YES
NO
NO
NO
NO
记录此题以提醒自己:贪心的代码大部分时间都会是
O
(
N
)
O(N)
O(N)扫过去一遍,不会再回头或是来回横跳,要以这个作为贪心策略的依据之一,如果你写了一个while
循环或是乱七八糟的多重循环,你就要怀疑你的贪心策略是不是出问题了。
简化一下此题的描述便是:给定一个长度为 n n n的序列 a a a,进行没有限制次数的操作,每次能够让当前下标的元素减 2 2 2,左右的元素减 1 1 1,问能否有一种做法使得序列全部变为 0 0 0。
首先:操作仅能够在 2 2 2 ~ n − 1 n-1 n−1的下标位置进行(如果起始下标为 1 1 1),不然的话会越界,所以最终枚举的时候就直接枚举 2 2 2 ~ n − 1 n-1 n−1。
然后:怎么贪心才能够使得最终的结果尽量接近全 0 0 0 ? 如果让你从头到尾只能扫一遍而不能回头看,那么最好的贪心策略就是尽量减,能减多少减多少,并且同时要保证尽量不把任何一个数减成负的,因为如果有一个变成负的了,之后再怎么操作也没用了。
那怎么尽量减呢:扫 2 2 2 ~ n − 1 n-1 n−1,对于每个元素首先取前一个元素和当前元素除 2 2 2的最小值为 t t t,然后让前一个元素减去 t t t,当前个元素减去 2 t 2t 2t,后一个元素减去 t t t,这样就能最大限度保证不会变为负数,如果在这种情况下还会导致某个元素变为负的,或者是前一个元素减不成 0 0 0,那这个序列就一定没办法变为全 0 0 0了。在这里我们并不需要提前考虑后一个元素,因为后一个元素还会被后边元素影响,所以按照贪心的观念我们不去多思考,直接着手于眼前。
到最后还要特判一下没有在循环里判断的数。
代码:
#include<iostream>
using namespace std;
const int N = 2e5 + 10;
int a[N]; int n;
int main() {
int t; cin >> t;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
bool has_answer = 1;
for (int i = 2; i <= n - 1; i++) {
int sub = min(a[i - 1], a[i] / 2);
a[i - 1] -= sub;
a[i] -= 2 * sub;
a[i + 1] -= sub;
if (a[i-1] > 0 || a[i - 1] < 0 || a[i] < 0 || a[i + 1] < 0) {
has_answer = 0;
break;
}
}
if (a[n - 1] != 0 || a[n] != 0)has_answer = 0;
if (has_answer)cout << "YES\n";
else cout << "NO\n";
}
return 0;
}