【贪心】AcWing 1248. 灵能传输

https://www.acwing.com/problem/content/1250/

这类较复杂的贪心题目一般都需要对题目进行分析,形式化描述问题,然后再去转换问题的条件。

对于问题给定的灵能数组,a1, a2, ........ , an - 2, an - 1, an。

发现如果按照题目中给出的变换,

  • ai-1  = ai-1  + ai
  • ai     = -ai
  • ai+1 = ai+1 + ai

不影响数组的总和,这三个元素的和依然不变,而且最有意思的的是,他们的前缀和依次是:

  • ai-1 + ai
  • ai-1
  • ai+1 + ai + ai-1

下标的变化与变换前的下标是对应的,这意味着仅仅是前缀和的次序发生了变化。对于整个数组仍是如此。

这样问题就归结为对于a数组的前缀和s而言,找到一个排序(除了最左最右元素)使得所有元素之间的差尽可能小。

这里我们重点关注前缀和数组中的最大值与最小值。假定最左边元素小于最右边元素。

我们要让最小值靠近左边,最大值靠近右边。否则从最左到最大以及最右到最大的间距一定不是最小(曲线更加陡峭)。

接下来就是确定从最大值到达最小值都要经过那些前缀和项(最大到最右同理)。

为了使得项之间的间距尽可能小,我们应该使得项均匀分布,即隔一个取一个。

最后再排列从最小值到达最大值的项就行了。得到了我们要找的排序。

(y总的代码贴上)

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 
 6 using namespace std;
 7 
 8 typedef long long LL;
 9 
10 const int N = 300010;
11 
12 int n;
13 LL a[N], s[N];
14 bool st[N];
15 
16 int main()
17 {
18     int T;
19     scanf("%d", &T);
20     while (T -- )
21     {
22         scanf("%d", &n);
23         s[0] = 0;
24         for (int i = 1; i <= n; i ++ )
25         {
26             scanf("%lld", &a[i]);
27             s[i] = s[i - 1] + a[i];
28         }
29 
30         LL s0 = s[0], sn = s[n];
31         if (s0 > sn) swap(s0, sn);
32         sort(s, s + n + 1);
33 
34         for (int i = 0; i <= n; i ++ )
35             if (s[i] == s0)
36             {
37                 s0 = i;
38                 break;
39             }
40 
41         for (int i = n; i >= 0; i -- )
42             if (s[i] == sn)
43             {
44                 sn = i;
45                 break;
46             }
47 
48         memset(st, 0, sizeof st);
49         int l = 0, r = n;
50         for (int i = s0; i >= 0; i -= 2)
51         {
52             a[l ++ ] = s[i];
53             st[i] = true;
54         }
55         for (int i = sn; i <= n; i += 2)
56         {
57             a[r -- ] = s[i];
58             st[i] = true;
59         }
60         for (int i = 0; i <= n; i ++ )
61             if (!st[i])
62                 a[l ++ ] = s[i];
63 
64         LL res = 0;
65         for (int i = 1; i <= n; i ++ ) res = max(res, abs(a[i] - a[i - 1]));
66 
67         printf("%lld\n", res);
68     }
69 
70     return 0;
71 }
72 
73 作者:yxc
74 链接:https://www.acwing.com/activity/content/code/content/196127/
75 来源:AcWing
76 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

posted @ 2020-10-14 23:15  褪色回音  阅读(167)  评论(0)    收藏  举报