例题8-3 4 Values whose Sum is 0

/*灵魂写手soul-editor:wn------王宁*/
#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
int a[4002];
int b[4002];
int c[4002];
int d[4002];
int sum;
int n;
bool first=1;
void solve()
{
	/*如果是第一次就不用输出用于分割答案的换行符*/
	if(first) first=0;
	else printf("\n");
	sum=0;
	vector<int> u; vector<int> v; u.clear(); v.clear();
	vector<int>::iterator p,q;
	/*记录一组内有多少个需要检验的元素*/
	int acc=-1;
	/*本解题方法的核心,用于剪枝的两个变量
	经过排序后,最左边是最小的元素,必定需要从另外一组
	的最右边的最大元素中找能产生和为0的元素。然而当轮到下一个元素去找
	另一个元素的时候,它不必从最右边重新开始找,只需要从上一个和能为0的元素的
	和第一次为0的位置开始找就行了。jb就是记录边界,flag用于确定它是第一次。
	即使是到了本组的右边,这条也是适用的。
	内心思考活动:到了右边,假设元素是正的,需要到左边去找。这时jb假设也已经移到左边了。
	相加等于0,记录,下一个,因为变大,所以需要找更小的,另一组右边的的没用。所以仍然成立*/
	int flag=1;
	int jb;
	int i,j;
	for(i=1;i<=n;++i)
	{
		for(j=1;j<=n;++j)
		{
			u.push_back(a[i]+b[j]);
			v.push_back(c[i]+d[j]);
			++acc;
		}
	}
	sort(u.begin(),u.end());
	sort(v.begin(),v.end());
	jb=acc;
	for(i=0;i<=acc;++i)
	{
		flag=1;
		//每一次搜索的边界都要移动
		for(j=jb;j>=0;--j)
		{
			if(u[i]+v[j]==0)
			{
				++sum;
				if(flag)
				{
					flag=0;
					jb=j;
				} 
			}
			//另一个剪枝:主元素固定,辅助元素在变小,只要还大于0就还有机会,小于0就绝对不行了。
			else if(u[i]+v[j]<0)
			break;
		}
	}
	printf("%d\n",sum);
}
int main()
{
	int runs,run,i,j;
	char sh[10];
	scanf("%d",&runs);
	for(run=1;run<=runs;++run)
	{
		gets(sh);
		scanf("%d",&n);
		for(i=1;i<=n;++i)
		{
			scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
		}
		solve();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/JXUFE_ACMer/article/details/81354598
今日推荐