东华复试67三角形的个数

三角形的个数

问题描述
明明的爸爸常用玩游戏的方法来激发明明对几何学的兴趣。这天明明的爸爸和明明又玩起了有关三角形的游戏。

明明爸爸对明明说:“我们能不能构造一个周长为15的三角形?” “太简单了,”明明说道:“三条边长都是5的三角形,它的周长不就是15吗?” “明明真聪明,算得真快。”明明爸爸接着说:“可是,我不想要三条边都相等的三角形哪!” 明明大眼睛一转,说道:“那也好办啊,我只要对这个等边三角形的一条边减去一个数,再把这个数加到另一条边上就可以得到一个新的周长为15的三角形。例如,在第一条边上减去1,在第二条边上加上1,这样不就可以得到一个周长为15的新的三角形了吗?” “哇,明明太聪明了”爸爸称赞道。“对,如果把第一条边上减去的1加到第三条边上去不就又可以得到周长为15的另外一个新三角形了吗?”爸爸模仿着明明的方法和语气。 “不对呀,爸爸。你构造的三角形和我构造的三角形是同样的三角形。爸爸你看,我的三角形三条边分别长为4、6、5,而你的三角形三条边分别长为4、5、6,将三条边按其边长排序后都得到4、5、6,所以它们是同一个三角形,不是两个不同的三角形。” “啊,还是明明聪明。那还有没有其他周长为15的三角形吗?” “当然有啦。三条边边长分别为4、4、7的三角形,它的周长就是15,不过你可能不喜欢它,因为它有两条边的边长相等。” 明明和爸爸玩了一下午这样的三角形游戏,明明一共又构造了另外两个他认为他爸爸喜欢的三角形,即边长分别为2、6、7的三角形和边长分别为3、5、7的三角形。

晚上,明明躺在床上还在思考:如果周长不是15,而是90,那么爸爸喜欢的三角形有多少个呢?
明明的问题可以归结为:根据一个正整数n(3 ≤ n ≤ 100),要求统计出同时满足下列条件的三角形的个数:

边长都是整数。
周长为n。
边长两两不相等。
之所以有上述第一个条件,那是因为明明只知道正整数,没有学过分数和实数,因此他构造出的三角形的边长均为正整数。 请你写一个程序来帮助明明计算出他认为他爸爸喜欢的三角形的个数。

输入说明

你写的程序需要从标准输入设备(通常为键盘)中读入多组测试数据,每组测试数据仅占一行,每行仅包括一个正整数n(3 ≤ n ≤ 100),代表需要被统计的三角形的周长,n的前后都没有任何空格。每组测试数据与其后一组测试数据之间没有任何空行,第一组测试数据前面以及最后一组测试数据后面也都没有任何空行。

输出说明

对于每一组测试数据,你写的程序要求计算出一组相应的运算结果,并将每组运算结果依次写入到标准输出设备(通常为启动该程序的文本终端,例如Windows中的命令行终端)中。每组运算结果为一个大于等于0的整数构成,即满足条件的三角形的个数。例如:测试数据n为15时,运算结果应为3。输出时,每组运算结果占一行,其行首和行尾都没有任何空格或其他字符,每组运算结果与其后一组运算结果之间没有任何空行或其他字符,第一组运算结果前面以及最后一组运算结果后面也都没有任何空行或其他字符。

思路解释

目前最让我大脑颤抖的一题,想死磕去重的思路不查答案导致卡了 2个多小时 ,通过调试和测试输出终于找到了输出结果的规律并成功AC。
下面先看在输入 30 的情况下,所有的可能三角形:
下图划线部分是作为结果个数的输出,共计 12 个,满足了测试用例的情况。而其他的输出虽然满足三角形的定义,但是属于边长重复的情况。
在这里插入图片描述

设边

设三条边为 l1, l2, l3:
观察数据可以发现,咋们可以让第一条边限制在 l1 <=n / 3 - 1以内,即 9 以内。而第二条边都是大于等于 9 的,故应该为 l2 >= n / 3 - 1,l3 = l1 - l2

l2 和 l3的关系及其去重

观察 l2 和 l3 之间的关系,可以发现,在同一个 l1 下, l2 和 l3 在不同的输出下,是对称的,即当 l1 = 3 时, l2 = 13,则 l3 = 14,如果 l2 = 14,则 l3=13。
从这个关系我们可以有如下思路:通过创建一维数组 A[n] = {0},当找到一个答案时(比如 3, 13, 14),就把 A[13] = 1,而一旦找到下一个输出结果时(3,14,13),我们就判断 A[13] 是否等于 0,是,则加入结果,不是,则直接跳过当前循环,实现去重。

还原

每当走过一个 l1 的循环之后,我们要把除 l1 之外的所有 A[i] 都还原回 0,避免下一轮去除了过多的结果,导致答案错误。(例如,如果我们没有还原,那么A[13] 还是 1,后续其他结果中如果出现边为13,但不是3,13,14的情况的就无法被加入到结果中了。)

l3 的判断

而 l3 会随着 l2 的增大而逐渐减小,但我们可以从划线部分发现,所有的 l3 都是大于10的,而被我们去除的重复项中 l3 都是小于等于 10 的,对于刚好等于 10 的情况可以被 l2进行去重,而小于 10 的情况,我们无需对它们进行判断,因为我们每轮结束都会把A[l1] = 1,而 l1 是小于等于 9 的,这样就相当于把小于10的部分提前去除了。

代码实现

#include<stdio.h>
#include<math.h>
int isFun(int x, int y, int z) {//判断是否是三角形,且没有重复边
    if(x + y <= z || x + z <= y || y + z <= x) {
        return 0;
    }
    if(abs(x - y) >= z || abs(x - z) >= y || abs(y - z) >= x) {
        return 0;
    }
    if(x == y || y == z || x == z) {
        return 0;
    }
        return 1;
}
int main(){
    int n;
    while(scanf("%d", &n) != EOF) {
        int count = 0;
        int A[n] = {0};//一维数组,用于判断是否已经访问
        for(int i = 1; i < n/3; i++) {// l1 从 1 到 n/3 - 1
            for(int j = n/3 - 1; j < n; j++) { //l2 >= n/3-1
                 if(isFun(i,j,n-i-j)) {
                        if(A[j] == 1 || A[n - i - j] == 1) {//如果有重复就直接去重
                            continue;
                        }
                    count++;
                    A[j] = 1;//将已经加入结果的l2设置为1,用于去重
                }
            }
                    A[i] = 1;//所有的l2遍历完毕,把l1设置为1
                    for(int j = i + 1; j < n; j++) {//还原除l1外的所有边的状态
                        A[j] = 0;
                    }
        }
    printf("%d\n",count);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值