题目 2307: 蓝桥杯2019年第十届省赛真题-灵能传输
题目
在游戏《星际争霸 II》中,高阶圣堂武士作为星灵的重要 AOE 单位,在 游戏的中后期发挥着重要的作用,其技能”灵能风暴“可以消耗大量的灵能对 一片区域内的敌军造成毁灭性的伤害。经常用于对抗人类的生化部队和虫族的 刺蛇飞龙等低血量单位。
你控制着 n 名高阶圣堂武士,方便起见标为 1, 2, · · · , n。每名高阶圣堂武士 需要一定的灵能来战斗,每个人有一个灵能值 ai 表示其拥有的灵能的多少(ai 非负表示这名高阶圣堂武士比在最佳状态下多余了 ai 点灵能,ai 为负则表示这 名高阶圣堂武士还需要 −ai 点灵能才能到达最佳战斗状态)。现在系统赋予了 你的高阶圣堂武士一个能力,传递灵能,每次你可以选择一个 i ∈ [2, n − 1],若 ai ≥ 0 则其两旁的高阶圣堂武士,也就是 i − 1、i + 1 这两名高阶圣堂武士会从 i 这名高阶圣堂武士这里各抽取 ai 点灵能;若 ai < 0 则其两旁的高阶圣堂武士, 也就是 i − 1, i + 1 这两名高阶圣堂武士会给 i 这名高阶圣堂武士 −ai 点灵能。形 式化来讲就是 ai−1+ = ai, ai+1+ = ai, ai− = 2ai。
灵能是非常高效的作战工具,同时也非常危险且不稳定,一位高阶圣堂武士拥有的灵能过多或者过少都不好,定义一组高阶圣堂武士的不稳定度为
maxni=1∣ai∣{max}_{n}^{i=1}|a_i|maxni=1∣ai∣ ,请你通过不限次数的传递灵能操作使得你控制的这一组高阶圣堂武 i=1士的不稳定度最小。
输入
本题包含多组询问。输入的第一行包含一个正整数 T 表示询问组数。 接下来依次输入每一组询问。
每组询问的第一行包含一个正整数n,表示高阶圣堂武士的数量。 接下来一行包含n个数a1,a2,··· ,an。
(对于所有评测用例,T ≤ 3,3 ≤ n ≤ 300000,|ai| ≤ 109。)
输出
输出 T 行。每行一个整数依次表示每组询问的答案
样例输入
3
3
5 -2 3
4
0 0 0 0
3
1 2 3
样例输出
3
0
3
解题思路
本题可以DFS列举,但时间肯定会超限。于是,看到一维数列,想到了采用前缀和,对于给定的数列a1—an,其前缀和可以表示为s[1]-s[0]、s[2]-s[1]…s[n]-s[n-1](其中s[0]为0),这样,题目要求的maxni=1∣ai∣{max}_{n}^{i=1}|a_i|maxni=1∣ai∣ 就转化为了求前缀和相邻两项之差的最大绝对值的最小值。
再来看题目中规定的变换:ai−1+ = ai, ai+1+ = ai, ai−=2ai,反映到前缀和即为:si-1+=ai,si-=ai,因此,如果发生题中所述的变换,即为将原来的si-1和si的值进行交换(使用前缀和之差的好处得以显现,交换操作被简化),而交换并不影响si+1的值。
最后,要求“前缀和相邻两项之差的最大绝对值的最小值”:由于s[0]始终为0不可更改,s[n]又是一个定值,不可交换(因为a[n-1]是最后一个“可选择的高阶圣堂武士”),故本题转变为一个“s[0]、s[n]为定值,s[1]-s[n-1]为已知,通过交换s[1]-s[n-1],使得数组s相邻两个数之间差的绝对值最小的问题”。
显然,如果是一个数列a,所有元素都可以自由交换,则升序或者降序排列可使得相邻元素之差绝对值最小;借鉴此思路,我们假设s[0] = min(s[0],s[n]),s[n] = max(s[0],s[n]),那么在s数列的s[0]、s[n]同样可以视为升序数列中的两个点,但原点和终点的位置在数列中间某处,s中的其余数值按照升序排列放入其中,如下图所示:
但我们会发现,原点和终点之间的差值一定会很大,这显然不符合我们要求,所以想到“交叉放置”升序数列中的s[i](i!=0且i!=n)(没有数学推理)。比如,n=9, s[10] = {0,1,-3,2,-6,-1,5,8,-4,4},由于s[0]<s[n],因此选择升序排列,排列后得到{-6,-4,-3,-1,0,1,2,4,5,8}。为了便于直观感受,我画出了最终调换后的数组,如下图所示:
根据实际给定数据的不同,需要注意s[0]和s[n]的大小关系,当前者大于后者时,对s进行降序排列;反之,升序排列。还需要注意最后可能不一定能“跳回”s[n],因此,若恰好无法跳回,注意补充给s[n]赋值。
(参考视频:https://www.bilibili.com/video/BV1Mb411t7M1?share_source=copy_web,感谢分享思路!)
易错点
- Lmax函数中,“跳着取”s[i]的值的下标需要注意,以及跳出循环的条件需要注意;
- 求出的前缀和可能很大,因此需要以long int来存储,相应的,qsort当中的cmp函数也需要重写,不可以将long int类型直接相减的值作为返回值,否则将排序错误。
代码
#include<stdio.h>
#include<stdlib.h>
int cmp_up(const void *a, const void *b){long int c = *(long int *)a;long int d = *(long int *)b;if (c>d)//升序return 1;elsereturn -1;
}int cmp_d(const void *a, const void *b){long int c = *(long int *)a;long int d = *(long int *)b;if (c<d)//降序return 1;elsereturn -1;
}long int int_abs(long int a){//返回long int类型的绝对值return (a>=0)?a:-a;
}long int Lmax(int n, long int s[],int sub_s0, int sub_sn){int i,k=1;long int max=0,temp=0;long int a[n+1];a[0] = 0;//避免0是最小数,a[0]未赋值的情况a[n] = s[sub_sn];//printf("%d %d",sub_s0,sub_sn);for (i=sub_s0-2;i>=0;i-=2)//从s0到s_min/s_max{a[k++] = s[i];if (i<=1)break;}for (i = (i==0)?1:0;i<sub_s0;i+=2)//从s_min到s_0{a[k++] = s[i];if ((i+2)>=sub_s0)break;}for (i = sub_s0+1;i<sub_sn;i++)//中间部分a[k++] = s[i];for (i = sub_sn+1;i<=n;i+=2)//从sn后面的第一个到末尾{a[k++] = s[i];if((i+2)>n)break;}for (i = (i==n)?(n-1):n;i>=sub_sn;i-=2)//从smax回到sn{a[k++] = s[i];if ((i-2)<sub_sn)break;}for (i=1;i<=n;i++)//遍历查找最大值{temp = int_abs(a[i]-a[i-1]);//printf("%ld",temp);if (temp>max)max = temp;}return max;
}int main()
{int T,i,n,j,sub_s0,sub_sn;long int sn;scanf("%d",&T);for (i=0;i<T;i++){scanf("%d",&n);long int a[n+1],s[n+1];//累加和可能是long intsub_s0=-1;sub_sn=-1;s[0] = 0;//为了便于求第一项前缀和for (j=1;j<=n;j++){scanf("%ld",&a[j]);s[j] = s[j-1]+a[j];//求前缀和}sn = s[n];//记录下sn的值,便于之后查找if (sn>=0)//如果sn>s0,那么升序排列qsort(s,n+1,sizeof(long int),cmp_up);else//反之,降序排列qsort(s,n+1,sizeof(long int),cmp_d);for (j=0;j<=n;j++)//sub_sn>sub_s0{if (sub_sn==-1 && s[j]==sn)sub_sn = j;else if (sub_s0==-1 && s[j]==0)sub_s0 = j;if (sub_s0>-1 && sub_sn>-1)//找到了下标break;}printf("%ld\n",Lmax(n,s,sub_s0,sub_sn));}return 0;
}
错误代码
以下代码是错误代码(43分),主要问题是求前缀和的数列没有用long int类型,且Lmax函数中的“跳着取”的边界条件错误。
#include<stdio.h>
#include<stdlib.h>
int cmp_up(const void *a, const void *b){return *(int *)a - *(int *)b;//升序
}int cmp_d(const void *a, const void *b){return *(int *)b - *(int *)a;//降序
}int int_abs(int a){return (a>=0)?a:-a;
}int Lmax(int n, int s[],int sub_s0, int sub_sn){int i,k=1,max=0,temp=0;int a[n+1];a[0] = 0;//避免0是最小数,a[0]未赋值的情况for (i=sub_s0-2;i>1;i-=2)//从s0到s_min/s_maxa[k++] = s[i];for (i = (i==0)?1:0;i<(sub_s0-2);i+=2)//从s_min到s_0a[k++] = s[i];for (i = sub_s0+1;i<sub_sn;i++)//中间部分a[k++] = s[i];for (i = sub_sn+1;i<=(n-2);i+=2)//从sn后面的第一个到末尾a[k++] = s[i];for (i = (i==n)?(n-1):n;i>=(sub_sn+2);i-=2)a[k++] = s[i];a[n] = s[sub_sn];for (i=1;i<=n;i++){temp = int_abs(a[i]-a[i-1]);if (temp>max)max = temp;}return max;
}int main()
{int T,i,n,j,sub_s0,sub_sn;int sn;scanf("%d",&T);for (i=0;i<T;i++){scanf("%d",&n);int a[n+1],s[n+1];sub_s0=-1;sub_sn=-1;s[0] = 0;//为了便于求第一项前缀和for (j=1;j<=n;j++){scanf("%d",&a[j]);s[j] = s[j-1]+a[j];//求前缀和}sn = s[n];//记录下sn的值,便于之后查找if (sn>0)qsort(s,n+1,sizeof(int),cmp_up);elseqsort(s,n+1,sizeof(int),cmp_d);for (j=0;j<=n;j++)//sub_sn>sub_s0{if (sub_sn==-1 && s[j]==sn)sub_sn = j;else if (sub_s0==-1 && s[j]==0)sub_s0 = j;if (sub_s0>-1 && sub_sn>-1)//找到了下标break;}printf("%d\n",Lmax(n,s,sub_s0,sub_sn));}return 0;
}
题目 2307: 蓝桥杯2019年第十届省赛真题-灵能传输相关推荐
- 题目 2311: 蓝桥杯2019年第十届省赛真题-Fibonacci 数列与黄金分割
题目 Fibonacci 数列是非常著名的数列: F[1] = 1,F[2] = 1, 对于 i > 3,F[i] = F[i − 1] + F[i − 2] Fibonacci 数列有一个特殊 ...
- [蓝桥杯]2019年第十届省赛真题C/C++ B组 填空+大题
第十届蓝桥杯省赛题目 填空A:组队 填空B:年号字串 填空C:数列求值 填空 D: 数的分解 填空 E: 迷宫 大题F:特别数的和 大题G:完全二叉树的权值 大题H:等差数列 大题I-后缀表达式 填空 ...
- 蓝桥杯2019年第十届国赛真题-大胖子走迷宫
题目 题目链接 题解 BFS. 整体思路:将位置信息和时刻信息放入队列,根据时刻信息判断当前时刻小明的大小,如果大小为1×11×11×1则不能原地停留,因为没意义啊,停留是为了让自己的肚子减小,但是1 ...
- 蓝桥杯题目 2682: 蓝桥杯2022年第十三届省赛真题-GCD
题目描述 给定两个不同的正整数 a, b,求一个正整数 k 使得 gcd(a + k, b + k) 尽可能大,其中 gcd(a, b) 表示 a 和 b 的最大公约数,如果存在多个 k,请输出所有满 ...
- 蓝桥杯2022年第十三届省赛真题-纸张尺寸
题目描述 在 ISO 国际标准中定义了 A0 纸张的大小为 1189mm × 841mm,将 A0 纸沿长边对折后为 A1 纸,大小为 841mm × 594mm,在对折的过程中长度直接取下整(实际裁 ...
- 消除游戏——蓝桥杯2022年第十三届省赛真题
题目描述 在一个字符串 S 中,如果 S i = S i−1 且S i 不等于S i−1,则称 S i 和 S i+1 为边缘字符.如果S i 不等于S i−1且 S i = S i+1,则 S i− ...
- 每日一题——质因数个数(蓝桥杯2022年第十三届省赛真题)
如何将一个正整数分解质因数:每日一题--将一个正整数分解质因数_笨小古的博客-CSDN博客 题目描述:给定正整数 n,请问有多少个质数是 n 的约数. 输入格式:输入的第一行包含一个整数 n. 输出格 ...
- 蓝桥杯2022年第十三届省赛真题-选数异或
题目描述 给定一个长度为 n 的数列 A1, A2, · · · , An 和一个非负整数 x,给定 m 次查询, 每次询问能否从某个区间 [l,r] 中选择两个数使得他们的异或等于 x . 输入 输 ...
- 蓝桥杯嵌入式STM32G431——第七届省赛真题模拟液位检测告警系统
第七届省赛真题模拟液位检测告警系统 第七届省赛真题 主函数部分的代码功能实现(不包含各模块初始化代码) 第七届省赛真题 主函数部分的代码功能实现(不包含各模块初始化代码) #include " ...
最新文章
- 安卓高手之路之 GDI图形引擎篇
- 【Java面试题】docker拉取镜像
- centos7 网卡配置vlan_Centos7安装后的一些基础配置
- Atlas学习手记(9):异步调用Page Method
- C#实现渐变颜色的Windows窗体控件
- Kmplayer播放器 绿色免安装版 2016 中文版
- MIUI V5正式发布 全部功能展示PPT回看
- In_interrupt( ) 和In_irq( )【转】
- apache tomcat (catalina)查版本(solaris/unix)
- php中crypt怎么还原,PHP笔记 —— crypt方法
- docker用gpu的参数_初探Docker调用GPU
- 阿里云ECS利用密钥对ssh登录服务器
- 中国非制式爆炸物薄膜传感器研究取得进展
- iOS实现一个简单的视频播放器
- 加州大学洛杉机分校计算机科学,加州大学洛杉矶分校计算机科学
- 线上服务器老是卡,该如何优化?
- loj3059/bzoj5494/洛谷P5294 [HNOI2019]序列 单调栈+主席树
- 高中会考计算机都考啥,高中会考都有哪些科目
- java多态怎么学_Java学习笔记---多态
- matlab多重数值积分,Matlab数值积分和微分(一重、多重都有),简单实用。