等差数列

等差数列

给定一个长度为 n 的正整数数列 a1,a2,,an 和一个正整数 k

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

+ i x ,增加操作,将 ai 的值增加 xx1)。
- i x ,减少操作,将 ai 的值减少 xx1)。
要求:在任何时候,你都需要保证每个 ai 的值都是正整数

请你使用尽可能少的操作次数,使得数列能够满足:对于所有 i1i<n),ai+1ai=k 均成立。

请你输出所需要的最少操作次数以及对应的具体操作方案。

输入格式

第一行包含两个整数 n,k

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

输出格式

第一行输出一个整数 p,表示所需要的最少操作次数。

接下来 p 行,用来输出具体操作方案,每行输出一个具体操作 + i x 或 - i x。你需要保证输出的 ix 均为正整数,且 1in

如果最少操作次数的方案不唯一,则输出任意一种方案均可。

数据范围

3 个测试点满足 1n7
所有测试点满足 1n10001k10001ai1000

输入样例1:

4 1
1 2 1 5

输出样例1:

2
+ 3 2
- 4 1

输入样例2:

4 1
1 2 3 4

输出样例2:

0

输入样例3:

7 1
1 1 2 3 4 5 6

输出样例3:

6
+ 2 1
+ 3 1
+ 4 1
+ 5 1
+ 6 1
+ 7 1

 

解题思路

  先给出我的做法,当时做的时候没写出来,今天回来补题又写出来了。

  首先我们不考虑改变a1,可以发现就是要把序列变成一个以a1为首项,公差为k的等差数列,而现在a1固定,意味着其余项ai都是一个固定的值,因此如果原序列的aia1+(i1)k,那么就要改变一次,因此最小改变次数就是与对应项不同的个数。

  因此如果a1固定不变,那么答案也就固定不变,因此为了得到最小的改变次数,我们就需要改变a1。那么a1要改成什么数呢?对于原序列,我们先算出每一项所在公差为k的等差数列的首项,即算出每一项的a1=ai(i1)k,然后开个哈希表统计a1的次数,假设最后统计出出现次数最多的数值x,那么就把a1变成x。这是因为如果把a1变成x那么对于算出a1=x的项就不需要进行改变(因为ai=x+(i1)k),选择出现次数最多的x意味着需要改变的项越小,这是典型的贪心思想。

  需要注意的是,我们只统计正整数的a1,因为题目要求每个数都是正整数,如果某项算出的a1不是正整数,而a1只能是正整数,因此该项无论a1取什么都要改一次。

  AC代码如下,时间复杂度为O(n)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1010;
 5 
 6 int a[N];
 7 struct Node {
 8     char c;
 9     int x, val;
10 };
11 
12 int main() {
13     int n, m;
14     scanf("%d %d", &n, &m);
15     for (int i = 1; i <= n; i++) {
16         scanf("%d", a + i);
17     }
18     unordered_map<int, int> cnt;
19     cnt[a[1]]++;    // 一开始a[1]本身出现一次
20     for (int i = 2; i <= n; i++) {
21         int t = a[i] - (i - 1) * m;
22         if (t > 0) cnt[t]++;    // 只有正整数才统计
23     }
24     int x = a[1], maxv = 1; // 一开始默认不改变a[1]
25     for (auto &it : cnt) {
26         if (it.second > maxv) maxv = it.second, x = it.first;   // 找到出现次数最多的数
27     }
28     vector<Node> ans;
29     for (int i = 1; i <= n; i++) {
30         int t = a[i] - (x + (i - 1) * m);   // a[i]与x + (i - 1)*k的插值,如果等于0表示不需要改变
31         if (t > 0) ans.push_back({'-', i, t});
32         else if (t < 0) ans.push_back({'+', i, -t});
33     }
34     printf("%d\n", ans.size());
35     for (auto &it : ans) {
36         printf("%c %d %d\n", it.c, it.x, it.val);
37     }
38     
39     return 0;
40 }
复制代码

  再给出y总的做法。

  首先容易发现答案最大为n,因为每个数最多改变1次,实际上还可以让a1固定,改变剩余的n1个数,因此答案最大值就变成了n1。因此现在最多改变n1次,意味着至少有一项是不会改变的,因此我们可以枚举每一项,固定该项ai,然后再枚举一遍序列进行比较,如果ajai+(ji)k,那么aj就需要改变。通过枚举所有项就可以找到最小改变次数了。

  AC代码如下,时间复杂度为O(n2)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1010;
 5 
 6 int a[N];
 7 struct Node {
 8     char c;
 9     int x, val;
10 };
11 
12 int main() {
13     int n, m;
14     scanf("%d %d", &n, &m);
15     for (int i = 1; i <= n; i++) {
16         scanf("%d", a + i);
17     }
18     vector<Node> ans;
19     for (int i = 1; i <= n; i++) {
20         vector<Node> vec;
21         for (int j = 1; j <= n; j++) {
22             int t = a[i] + (j - i) * m;
23             if (t <= 0) {   // 每个数都要是正整数
24                 vec.clear();
25                 break;
26             }
27             if (a[j] > t) vec.push_back({'-', j, a[j] - t});
28             else if (a[j] < t) vec.push_back({'+', j, t - a[j]});
29         }
30         if (!vec.empty() && (ans.empty() || ans.size() > vec.size())) ans = vec;
31     }
32     printf("%d\n", ans.size());
33     for (auto &it : ans) {
34         printf("%c %d %d\n", it.c, it.x, it.val);
35     }
36     
37     return 0;
38 }
复制代码

 

参考资料

  AcWing 4780. 等差数列(AcWing杯 - 周赛):https://www.acwing.com/video/4557/

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