POJ No.2758-4 Values Whose Sum is 0(折半枚举)

POJ No.2758-4 Values Whose Sum is 0(折半枚举)

给定各有n个整数的四个数列A、B、C、D。要从每个数列中各取出1个数,使四个数的和为0.求出这样的组合的个数。当一个数列中有多个相同的数字时,把它们作为不同的数字看待。

限制条件

1<=n<=4000

|{数字的值)|<2^28

输入

N = 6

A={-45,-41,-36,-36,26,-32}

B={22,-27,53,30,-38,-54}

C={42,56,-37,-75,-10,-6}

D={-16,30,77,-46,62,45}

输出

5

(-45-27+42+30=0, 26+30-10-46=0, -32+22+56-46=0,-32+30-75+77=0, -32-54+56+30=0)

分析:从4个数列中选择的话总共有n^4种情况,所以全部判断一遍不可行。不过把他们对半分成AB和CD在考虑,这时候就可以快速确定。

从2个数列选择的话,只有n^2种组合,所以可以进行枚举。先从A、B中取出a、b后,为了使总和为0,需要从C、D中取出c+d=-(a+b)。因此先将C、D种取出数字的n^2种方法全部枚举,将这些和排好序,然后就可以用二分搜索。

这个算法的复杂度是O(n^2logn)

有时候,问题的规模较大,无法枚举所有元素的组合,但能够枚举一半元素的组合。此时,将问题拆成两半后分别枚举,在合并他们的结果,这种方法叫做折半枚举。

import java.util.Arrays;
import java.util.Scanner;

public class Algorithm {

	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		System.out.print("n=");
		int n = cin.nextInt();
		int[] A = new int[n];
		int[] B = new int[n];
		int[] C = new int[n];
		int[] D = new int[n];
		int[] CD = new int[n * n];
		input(n, A, B, C, D);
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				CD[i * n + j] = C[i] + D[j];
		Arrays.sort(CD);

		int res = 0;
		int cd;
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++) {
				cd = -(A[i] + B[j]);
				if (binarySearch(CD, cd))
					res++;
			}
		System.out.println(res);
	}

	static void input(int n, int[] A, int[] B, int[] C, int[] D) {
		Scanner cin = new Scanner(System.in);
		System.out.print("A=");
		for (int i = 0; i < n; i++)
			A[i] = cin.nextInt();
		System.out.print("B=");
		for (int i = 0; i < n; i++)
			B[i] = cin.nextInt();
		System.out.print("C=");
		for (int i = 0; i < n; i++)
			C[i] = cin.nextInt();
		System.out.print("D=");
		for (int i = 0; i < n; i++)
			D[i] = cin.nextInt();
	}

	public static boolean binarySearch(int[] CD, int key) {

		int low = 0;
		int high = CD.length - 1;
		while (low <= high) {
			int middle = (low + high) / 2;
			if (key == CD[middle]) {
				return true;
			} else if (key < CD[middle]) {
				high = middle - 1;
			} else {
				low = middle + 1;
			}
		}
		return false;
	}

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值