紧接着上一篇博文,我们谈一题前缀和的应用,这一题是第十届蓝桥杯第十届最后一题,难度没多大,就是有点难想到用前缀和.
我们既然说了用前缀和那么我们用前缀和来分析一下:
我们假设有一个序列 a1 , a2 , a3 这个序列的前缀和为 a1 ,a1+a2 , a1+a2+a3
如果 a2< 0, 那么我们经过一次灵能传输之后,可以变为 a1 + a2 , -a2 , a3 +a2 此序列的前缀和为 a1+a2 , a1 , a1+a2+a3
细心的你会发现 ,我们如果对 第i 个数进行灵能传输, 那么我们只需要将前缀和的第 i- 1 和第i 个位置交换一下就行了,然后在找出这个前缀和中相邻最大那个元素,我们还会发现,前缀和的最后一个元素,永远都不会变,那我我们进行操作的时候,只需要对前面的数进行全排列就行了,然后找出最小的那个排列,就解决了,这是最容易想到的解决方案了,不清楚能不能通过所用的实例。看代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 第十届灵能传输 {
// 存储结果
static int min = Integer.MAX_VALUE;
public static void main(String[] args) throws NumberFormatException, IOException {
// 可能会遇到大数量的数字 我们使用流读取数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 读入几组数据
int count = Integer.parseInt(br.readLine().trim());
// 存储最后结果
int[] result = new int[count + 1];
// 循环读取每组数据
for (int a = 1; a <= count; a++) {
int num = Integer.parseInt(br.readLine().trim());
String[] split = br.readLine().split(" +");
int[] sum = new int[split.length + 1];
for (int i = 1; i <= num; i++) {
// 对每组数据进行前缀和的计算
sum[i] = sum[i - 1] + Integer.parseInt(split[i - 1]);
}
// dfs的时候存储中间前缀和
int[] temp = new int[sum.length];
// 前缀和的最后一个元素都是不变的 固定数字
temp[sum.length - 1] = sum[sum.length - 1];
// 递归的时候记录sum数组里面的哪些数已经被用过
boolean[] visit = new boolean[sum.length];
//dfs
dfs(temp, sum, visit, 1);
result[a] = min;
min = Integer.MAX_VALUE;
}
for(int i = 1; i <= count; i++){
System.out.println(result[i]);
}
}
private static void dfs(int[] temp, int[] sum, boolean[] visit, int n) {
// 当我们n等于这个的时候 说明我们已经找到一种情况了 这个时候判断这个数是否比min小
if (n == sum.length - 1) {
int res = fun_res(temp, n);
// 如果小,那么进行替换
if (res < min) {
min = res;
}
// 一定要记得退出当前栈
return;
}
// 对于temp数组的每一个位置,我们每一种情况都要试试
for (int j = 1; j < sum.length - 1; j++) {
// 如果sum数组里面的第j个数我们没有用过
if (!visit[j]) {
// 标记 当前数已经被用过了
visit[j] = true;
temp[n] = sum[j]; // 把sum数组里面的值赋给temp数组
// 下面是非常重要的剪枝操作 入股没有这个步骤,那么可能会超时
int aaa = fun_res(temp, n); // 如果temp数组当前的结果值已经比min要大了 ,那么我们就不用进行下面的递归了
if (aaa > min) {
visit[j] = false; // 表示我们没有用 sum[j]当前的值
continue; // 结束本层循环
}
// dfs深搜
dfs(temp, sum, visit, n + 1);
// 深搜结束后,我们把当前的值设置为没用过,以备后来用
visit[j] = false;
}
}
}
// 此函数是找出 一个前缀和数组中 相邻两元素差的最大值, 绝对值要注意
public static int fun_res(int[] sum, int n) {
int max = Integer.MIN_VALUE;
for (int i = 1; i <= n; i++) {
if (Math.abs(sum[i] - sum[i - 1]) > max) {
max = Math.abs(sum[i] - sum[i - 1]);
}
}
return max;
}
}
附上我的运行结果
看到这里 ,我觉得你已经差不多懂了,下次见。