三角形加强版

题目描述

想必学弟学妹们都被小明的三角形坑的不惨吧, 为了锻炼下学弟学妹我决定再补一刀。

让你们来选三角形,有n条边让你选3条边凑成一个三角形,问有多少种可能

比如: 1 2 3 4 这4条边,就有 2 3 4 .这一种可能 。

输入

首先是一个 t , 代表有几组测试数据

接下来一个 n , 代表有多少条边

接下来n个整数代表每条边的长度 l

其中 0 < t <= 10 ,  0 < n <= 1e5 , 0 < l <= 1000

输出

输出方案数

样例输入 
2
4
1 2 3 4
4
3 3 3 3
样例输出 
1
4

 开始的时候看到这题是 一个组合问题,第一想法就是深搜,然而n最大有1e5,而深搜的深度有三层,尽管有优化排除大量情况,但任然不行,

dfs方法(TLE):

#include
int n, b[10010], t, a[4], sum = 0, flag = 0;
void nb(int x, int y)//快排(从小到大)
{
	if (x >= y)
		return;
	int left, right, temp, t;
	left = x; right = y; temp = b[x];
	while (left < right)
	{
		while (b[right] >= temp && left < right)
			right--;
		while (b[left] <= temp && left < right)
			left++;
		if (left < right)
		{
			t = b[left]; b[left] = b[right]; b[right] = t;
		}
	}
	b[x] = b[left]; b[left] = temp;
	nb(x, left - 1);
	nb(left + 1, y);
	return;
}
void dfs(int x, int y)
{
	
	if (x == 4)//当x等于4时即已经搜索到3条边
	{
		if (a[1] + a[2] > a[3])//满足两条较小边之和大于第三边
			sum++;
		//因为b数组已经从小到大排序了,若出现两条边小于第3边,则后面搜索的第三条边不用再进行判断了,提前结束第三条边的搜索
		else
			flag = 1;
		return;
	}
	for (int i = y; i <= n - 3 + x; i++)
	{
		a[x] = b[i];
		dfs(x + 1, i + 1);
		if (flag == 1)//出现两边之和小于第3边直接提前退出第3条边的搜索
		{
			flag = 0;
			return;
		}
	}
}
int main()
{
	scanf("%d", &t);
	while (t--)
	{
		sum = 0;
		scanf("%d", &n);
		for (int i = 1; i <= n; i++)
			scanf("%d", &b[i]);
		nb(1, n);//对b数组从小到大排序
		dfs(1, 1);//深度搜索
		printf("%d\n", sum);
	}
	return 0;
}

正确思路

我们注意到,这题边的长度最大只有1000,与搜索每一条边相比,遍历长度反而时间更快了,这里可以用一个数组标记每一个长度出现的次数,再前缀和统计,先嵌套两重循环遍历前两条边较小的边,根据前缀和下标小于前两条边和的长度的边的数量,再相乘即可,注意这里容易出错的地方是三条边中有其中长度相等的情况,这是需要特殊判断一下。

AC代码:

#include
int main()
{
	long long  n, t, I, i, j;
	scanf("%lld", &t);
	while (t--)
	{
		//book数组标记每个长度出现的次数,min,max找到边中最小的长度和最大的长度
		long long book[2001] = { 0 }, min = 1e9, max = -1e9, dp[3001] = { 0 }, sum = 0;
		scanf("%lld", &n);
		for (i = 1; i <= n; i++)
		{
			scanf("%lld", &I);
			book[I]++;
			if (max < I)//找到最大边的长度
				max = I;
			if (min > I)//找到最小边的长度
				min = I;
		}
		for (i = 1; i <= 2010; i++)//前缀和(注意前缀和的长度一定要大于2000,因为两边之和最大为2000)
		{
				dp[i] = dp[i - 1] + book[i];
		}
		for (i = min; i <= max; i++)//遍历每一个长度
		{
			if (book[i] <= 0)//没有这个长度的边直接跳过
				continue;
			for (j = i+1; j <= max; j++)
			{
				if (book[j] <= 0)
					continue;
				sum += book[i] * book[j] * (dp[i + j - 1] - dp[j]);//三条边长度不同
			}
			if (book[i] >= 2)//三条边有2条长度相同
			{
				sum += book[i] * (book[i] - 1) / 2 * (dp[2 * i - 1] - book[i]);
			}
			if (book[i] >= 3)//三条边长度都相同
			{
				sum += book[i] * (book[i] - 1) * (book[i] - 2) / 6;
			}
		}
		printf("%lld\n", sum);
	}
	return 0;
}

你可能感兴趣的:(题组,c语言)