减法操作

减法操作

给定一个包含 n 个非负整数的数列 a1,a2,,an

你可以对该数列进行以下两种减法操作:

  • 任选其中一个元素,并将该元素的值减去 2
  • 任选两个相邻元素,并将两个元素的值各减去 1

请你判断,能否经过一系列减法操作,使得数列中的所有元素都变为 0

输入格式

第一行包含整数 n

第二行包含 n 个非负整数 a1,a2,,an

输出格式

如果能够经过一系列减法操作,使得数列中的所有元素都变为 0,则输出 YES ,否则输出 NO 。

数据范围

6 个测试点满足 1n10
所有测试点满足 1n2×1050ai104

输入样例1:

4
1 2 1 2

输出样例1:

YES

输入样例2:

3
1 0 1

输出样例2:

NO

 

解题思路

  比赛的时候想到的思路是,既然要求最后整个序列的每个数都变成0,那么就构造出一种方案,看看能不能让每个位置上的数都变成0。从前往后遍历每一个数,如果这个数是偶数,那么只进行操作2就可以。如果这个数是奇数,那么至少要执行一次操作1,这里就规定每次执行操作1时都是与当前位置的下一个位置减1,且对该数只执行一次操作1,剩下的都执行操作2(如果是序列的最后一个数,由于没有下一位了,因此如果最后一个数是奇数,那么就不可以执行任何操作了,即无法将序列变成全0)。最后看一下每一个位置是否都是非负数并且都是偶数就可以了。

  补一下y总严格的证明。

  我们将所有的操作方案看作是一个集合,现在考虑一下最优解有什么性质,看能不能在一个子集中找到答案。首先操作顺序并不会影响结果,比如对于一个数,先减2再减1,或者是先减1再减2都不会影响这个数的最终结果。因此我们可以人为定义一个操作顺序,就是先执行所有的操作2再去执行操作1,我们就可以在这个子集中看一下是否存在一种方案使得序列变成0。同时对于相邻的两个数,如果这两个数执行了两次操作2,那么等价于这两个数分别执行一次操作1,因此在这个子集中我们还可以考虑另外一个子集,在这子集中同一对元素只会执行一次操作2,因此最终我们只需要在这个子集的子集里面考虑就可以了,如果这个子集中存在某个方案使得序列变成0,那么代价于执行完所有的操作2后所有元素都变成非负偶数才可以。

  我们可以从前往后找到第一个奇数,由于要变成偶数,那么就要对这个数执行操作2,有两种选择,要么与这个数后面一个数执行操作2,要么与这个数前面一个数执行操作2。如果是与这个数前面一个数执行操作2,由于前面这个数是偶数,因此操作完后就变成奇数,由于要变成偶数,因此也有两种选择,与后面一个数或者与前面一个数执行操作2。如果是选择后面一个数,那么相当于相邻的两个元素执行了两次操作2,而这种方案不在这个子集中,因此只能选择前面的数。以此类推,前面的数又从偶数变成奇数了,那么总是与前一个数执行操作2,直到是序列的第一个元素,第一个数变成奇数,此时它前面没有元素了,就无法执行操作2,也不能与后面一个数执行操作2,因此就无解了。因此我们从前往后枚举找到第一个奇数后,我们只能操作这个数和它后面一个数,所以操作方案是唯一的。

  如果从前往后枚举找到某个数是偶数,要么不执行操作2,要么执行两次操作2,并且这两次操作2分别是选择前面一个数和后面一个数。而与前面一个数执行完操作2后,前面那个数就变成奇数了,那么又要对前面这个数执行操作2,可以发现又变成上面所描述的无解的情况了。因此如果遇到偶数,我们不能进行操作2

  因此如果遇到奇数我们只能操作这个数和它后面一个数,如果数偶数我们不能操作。所以操作方案是唯一确定的。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 2e5 + 10;
 5 
 6 int a[N];
 7 
 8 int main() {
 9     int n;
10     scanf("%d", &n);
11     for (int i = 0; i < n; i++) {
12         scanf("%d", a + i);
13     }
14     
15     bool flag = true;
16     for (int i = 0; i < n; i++) {
17         if (a[i] < 0) { // 每个数要求是非负偶数
18             flag = false;
19             break;
20         }
21         if (a[i] & 1) { // 奇数
22             if (i == n - 1) flag = false;   // 最后一个数是奇数,而最后一个数后面没有数,无法执行操作2
23             a[i + 1]--; // 与后面一个数执行操作2
24         }
25     }
26     
27     printf("%s", flag ? "YES" : "NO");
28     
29     return 0;
30 }
复制代码

 

参考资料

  AcWing 4619. 减法操作(AcWing杯 - 周赛):https://www.acwing.com/video/4405/

posted @   onlyblues  阅读(63)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示