题目描述
想必学弟学妹们都被小明的三角形坑的不惨吧, 为了锻炼下学弟学妹我决定再补一刀。
让你们来选三角形,有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<stdio.h>
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<stdio.h>
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;
}