算法设计竞赛常用技巧-折半枚举

思想

问题规模较大时,无法枚举所有元素的组合,但能够枚举一半元素的组合,此时,将问题拆成两半后分别枚举,再合并他们的方法往往非常有效。

例如:

4 Values Whose Sum is 0(POJ No.2785)

:给出四个数列,要求每个数列中选出一个数,四个数的和为0,问总共有几种选法。当一个数组中有多个相同的数字时,把他们作为不同的数字看待
限制条件
1<=n<=4000
|(数字的值)<=28 |

样例
输入
n=6
A={-45,-41,-36,-36,26,-32}
B={22,-27,53,30,-38,-54}
C={42,6,-37,-75,-10,-6}
D={-16,30,77,-46,62,45}

输出
5

传统做法:枚举四个数,时间复杂度可以达到O(n4),所以肯定是不行的

折半枚举法:将他们对半分成AB和CD再考虑,就可以快速解决了,从两个数列中选择只有n2种组合,所以可以枚举。

A+B+C+D=0转化为A+B=-(C+D)处理,首先枚举所有的-(C+D)进行排序,然后枚举所有的(A+B)利用二分查搜索查找-(C+D)数组,算法复杂度只有O(n2logn)、

/*
6
-45 -41 -36 -36 26 -32
22 -27 53 30 -38 -54
42 6 -37 -75 -10 -6
-16 30 77 -46 62 45
*/
#include<bits/stdc++.h>
using namespace std;
int n,A[10000],B[10000],C[10000],D[10000],CD[1000000],AB[1000000];
long long ans;
int main(){
	cin>>n;
	
	for(int i=0;i<n;i++){
		cin>>A[i];
	}
	for(int i=0;i<n;i++){
		cin>>B[i];
	}
	for(int i=0;i<n;i++){
		cin>>C[i];
	} 
	for(int i=0;i<n;i++){
		cin>>D[i];
	} 
	//枚举 -(C+D)
	int k1=0; 
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			CD[k1++]=-(C[i]+D[j]); 
		}
	} 
	sort(CD,CD+k1);
	//枚举(A+B) 
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			int AB=A[i]+B[j];
			ans+=upper_bound(CD,CD+k1,AB)-lower_bound(CD,CD+k1,AB);//利用二分搜索找相等 
		}
	}
	cout<<ans<<endl;
	return 0;
}

用map实现

/*
6
-45 -41 -36 -36 26 -32
22 -27 53 30 -38 -54
42 6 -37 -75 -10 -6
-16 30 77 -46 62 45
*/
#include<bits/stdc++.h>
using namespace std;
int n,A[10000],B[10000],C[10000],D[10000],AB[1000000];
long long ans;
map<int,int> mp;
int main(){
	cin>>n;
	
	for(int i=0;i<n;i++){
		cin>>A[i];
	}
	for(int i=0;i<n;i++){
		cin>>B[i];
	}
	for(int i=0;i<n;i++){
		cin>>C[i];
	} 
	for(int i=0;i<n;i++){
		cin>>D[i];
	} 
	//枚举 -(C+D)
	int k1=0; 
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			mp[-(C[i]+D[j])]++;
		}
	} 
	//枚举(A+B) 
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			int AB=A[i]+B[j];
			ans+=mp[AB];
		}
	}
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值