CF1278C-Berry Jam-(前缀和)
https://vjudge.net/problem/CodeForces-1278C
题意:有2n瓶果酱,中间有一个楼梯隔开,从中间往左或右两边清空果酱,使得两种果酱的数量相等,最少要清空多少瓶
题解:
拿掉果酱只能从中间往左边或者往右边拿,并且只能连续拿,很容易想到前缀和。
可以将果酱分割成两部分,左右前缀和。
用-1替换果酱2,计算前后缀和的时候以0为标准,表示果酱的数量。
正数表示果酱1的数量>果酱2的数量,负数表示果酱2的数量>果酱1的数量,0表示果酱1=果酱2的数量
举例说明
原数据
1 2 1 2 2 2 1 2
修改2变成-1
1 -1 1 -1 -1 -1 1 -1
左右两边用前后缀和计算
1 0 1 0 -2 -1 0 -1
对于前缀和为x的情况,只需要找到后缀和为-x的位置,将二者之间的果酱取下,就能保证剩余的果酱类型,数量相同。
对于相同的x,前缀和的idx越大越好,表示从中间往左边取物品数量越少。后缀和的idx越小越好,表示从中间往右边取物品数量越少。这里有贪心思想。
import java.util.HashMap; import java.util.Map; import java.util.Scanner; /** * @Date 2022/4/6 */ public class Main { public static void main(String[] args) { Scanner scan = new Scanner(System.in); int t = scan.nextInt(); while (t-- > 0) { int n = scan.nextInt(); int[] prefix = new int[n + 1]; Map<Integer, Integer> prefixNumIdxMap = new HashMap<>(); //左半部分 for (int i = 1; i <= n; i++) { int x = scan.nextInt(); if (x == 1) { prefix[i] = prefix[i - 1] + 1; } else { prefix[i] = prefix[i - 1] - 1; } //前缀的值,对应的索引i越来越大,越大越好,表示从中间往左边取物品数量越少 prefixNumIdxMap.put(prefix[i], i); } //右半部分输入,将2替换成-1,用于计算后缀和 int[] suffix = new int[n + 2]; Map<Integer, Integer> suffixNumIdxMap = new HashMap<>(); for (int i = 1; i <= n; i++) { int x = scan.nextInt(); if (x != 1) { x = -1; } suffix[i] = x; } for (int i = n; i >= 1; i--) { suffix[i] += suffix[i + 1]; //后缀的值,对应的索引i越来越小,越小越好,表示从中间往右边取物品数量越少 suffixNumIdxMap.put(suffix[i], i); } int ans = 2 * n; //前后缀和封顶是-n和n for (int i = -n; i <= n; i++) { if (prefixNumIdxMap.containsKey(i) && prefixNumIdxMap.get(i) != 0 && suffixNumIdxMap.containsKey(-i) && suffixNumIdxMap.get(-i) != 0 || i == 0) { ans = Math.min(ans, n - prefixNumIdxMap.getOrDefault(i, 0) + suffixNumIdxMap.getOrDefault(-i, n + 1) - 1); } } System.out.println(ans); } } }