枚举

ACM算法精华总结 算法从入门到放弃系列笔记
Study Date : 2020-01-20

1 算法思想

枚举(Enumerate)是基于现有知识来推断问题的解空间,遍历解空间验证是否符合问题描述的要求,从而对问题求解的思路。

2 解题要点

  • 给出解空间
    对问题建立数学模型,思考可能会出现的情况,需要枚举哪些要素。

  • 缩小解空间
    不是解空间中所有的元素都需要枚举出来进行验证,在枚举前应尽可能的缩小解空间,减少时间开销。

  • 选择合适的枚举顺序
    根据题目判断。比如要求的是最大的符合条件的素数,那自然是从大到小枚举比较合适。

3 例题

以下是一个使用枚举解题与优化枚举范围的例子。

题目

一个数组中的数互不相同,求其中和为0的数对的个数

题解

  • 确定解空间

枚举两个数的代码很容易就可以写出来。

	int length = array.length,ans=0;
	for (int i = 0; i < length; ++i){
		for (int j = 0; j < length; ++j){
			if (array[i] + array[j] == 0) ++ans;
		}
	}
  • 缩小解空间

原问题的答案由两部分构成:两个数相等的情况和不相等的情况。

由于题目已经说明数组中的元素互不相等且未要求数对的有序性,若数对<a,b>是题目的解,则<b,a>一定是题目的解。那么不妨要求所求的数对<a,b>一定满足a在数组中的位序< b在数组中的位序。

代码如下:

	int length = array.length,ans=0;
	for (int i = 0; i < length; ++i){
		for (int j = 0; j < i; ++j){ //  内层循环不再遍历整个数组,改为遍历外层循环遍历过的元素
			if (array[i] + array[j] == 0) ++ans;
		}
	}
	ans<<1; //在最终于结果上*2

接着考虑到数组中每个元素互异,因此对于给定第一个枚举值,数对的另一个值是唯一确定的,因此当内存循环遍历到题目条件符合描述的解时即可跳出内层循环。

	int length = array.length,ans=0;
	for (int i = 0; i < length; ++i){
		for (int j = 0; j < i; ++j){ //  内层循环不再遍历整个数组,改为遍历外层循环遍历过的元素
			if (array[i] + array[j] == 0) {
				++ans;
				break;
			}
		}
	}
	ans<<1; //在最终于结果上*2

虽然已经减少了解解空间,但还不是最优的结果。
基于上面当数对其中一个枚举确定时,另一个枚举值的取值也唯一确定的思路,如果能找到一种方法直接判断题目要求的那个数是否存在,就可以省掉枚举后一个数的时间了。

	// 1.求数组绝对值最大的数
	int absoultMaxValue = Arrays.stream(data).map(Math::abs).max().getAsInt();
	
	// 2.用于查询的boolean数组,注意List的 注意indexOf是通过遍历实现的
	boolean[] indexArray = new boolean[absoultMaxValue * 2 + 1];// 注意indexArray需要长度为absoultMaxValue * 2 + 1,+1的原因是因为0坐标
	Arrays.stream(data).forEach(d -> {
	    indexArray[absoultMaxValue + d] = true;
	});
	
	// 3.遍历数据数组,根据boolean数组判断每个元素是否存在使其满足条件的第二个枚举存在
    int ans = Arrays.stream(data).reduce(0, (acc, item) -> {
        if (indexArray[absoultMaxValue + 0 - item]) acc++;
        return acc;
    });
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值