【贪心】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 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

浙公网安备 33010602011771号