AcWing 1248. 灵能传输
考察:贪心
这道题和均分纸牌有点像,但是做法不完全一样
思路:
参考均分纸牌问题,考虑操作对前缀和数组的影响,可以发现每交换一次sum[i-1]变成了sum[i],而sum[i]变成了sum[i-1](0<i<n),根据取值范围可以发现i=0与i=n是不能交换的,所以我们只能分配sum[i](0<i<n)的位置,使得sum[i]-sum[i-1]尽可能小.假设sum[0]是最小值,sum[n]为最大值,如果要让差值最小,需要sum数组曲线呈递增排列(可以画图证明).但如果不是最值,我们需要让曲线尽量保持单调,假设sum[0]<sum[n],那么最小值放在sum[0]一侧,最大值放在sum[n]一侧,可以使得差值更小.
由图,我们要沿着蓝线在数轴上取值(不重复),构成一个新的序列(开头必为sum[0],结尾必为sum[n]),使得此序列sum[i]-sum[i-1]最小.可以发现从sum[0]~min要走一段重复的路程,在这段路程上我们要在上面取点sum[i],很显然我们需要有选择地取点,使得每个相邻的sum[i]差值最小.此处的贪心策略是每两个取一个点.如果sum[0]->min处就取所有点,那么返回时就只能直接从min~sum[1],此时跨度一定比每两个去点大.沿着蓝线可以发现我们可能会经过一次sum[n],但是sum[n]是一定放在最后的,因此我们可以采用双指针算法从sum[0]和sum[n]都开始取点.为了避免重复,可以用bool数组标记.
这里注意是假设了sum[0]<sum[n],所以让min靠近sum[0],如果sum[0]>sum[n]就与之相反.
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int N = 300010; 6 typedef long long LL; 7 int n; 8 bool st[N]; 9 LL ans,sum[N],a[N]; 10 int main() 11 { 12 int T; 13 scanf("%d",&T); 14 while(T--) 15 { 16 scanf("%d",&n); 17 ans = 0; 18 sum[0] = 0; 19 memset(st,0,sizeof st); 20 for(int i=1;i<=n;i++) scanf("%lld",&sum[i]),sum[i]+=sum[i-1]; 21 LL f = sum[0],e = sum[n]; 22 int l = 0,r = n; 23 if(f>e) swap(f,e);//如果不互换,那么s[0]向左找的时候可能与s[n]重叠. 24 sort(sum,sum+n+1); 25 for(int i=0;i<=n;i++) 26 if(sum[i]==f) 27 { 28 f = i; 29 break; 30 } 31 for(int i=n;i>=0;i--) 32 if(sum[i]==e) 33 { 34 e = i; 35 break; 36 } 37 for(int i=f;i>=0;i-=2)//s[0]放在第一个 38 a[l++] = sum[i],st[i] = 1; 39 for(int i=e;i<=n;i+=2)//s[n]放在最后一个 40 a[r--] = sum[i],st[i] = 1; 41 for(int i=0;i<=n;i++) 42 if(!st[i]) a[l++] = sum[i]; 43 for(int i=1;i<=n;i++) 44 ans = max(ans,abs(a[i]-a[i-1])); 45 printf("%lld\n",ans); 46 } 47 return 0; 48 }