第十届蓝桥杯--灵能传输

紧接着上一篇博文,我们谈一题前缀和的应用,这一题是第十届蓝桥杯第十届最后一题,难度没多大,就是有点难想到用前缀和.
第十届蓝桥杯--灵能传输_第1张图片
我们既然说了用前缀和那么我们用前缀和来分析一下:
我们假设有一个序列 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;

	}

}

附上我的运行结果
第十届蓝桥杯--灵能传输_第2张图片

看到这里 ,我觉得你已经差不多懂了,下次见。

你可能感兴趣的:(算法与数据结构)