您当前的位置:首页 > 计算机 > 编程开发 > Java

一道有趣的Java编程题

时间:02-06来源:作者:点击数:

原题为:

java书上选择结构章节后,有一个课后习题,题目描述是下面这样的:

在这里插入图片描述

仔细一审题,人家是在学java书的选择结构呢,就是说java还没入门呢,所以这道题对于他来说肯定是很难的,即使对我这样的老手来说,也是花了好一些时间完成的,因为这道题有细节,细节上容易出错,如果审题没审好,没理解好题目逻辑也容易出错,所以这里记录一下,方便大家学习,大家尽量先按自己的思考做一遍,每个人都有不同的想法和解法,自己思考过之后才会理解更深,实在写不出来的时候再来参考我的答案。

这里我使用Kotlin语言实现,不懂kotlin也没关系,看完我的思路之后,你自己用Java代码来实现一次完全可以的,这时你会发现java实现起来比kotlin的代码要多很多,这就是为什么Google在2017年的时候把Kotlin当成Android开发的官方语言了,大家有时间也应该学习一下Kotlin,只要是使用java开发的(比如后端开发),都可以使用kotlin来替换的,因为Kotlin最终编译后也是class字节码,底层其实就是java。我的实现代码没多少,我写了很多注释,还有空行,看起来多,其实重点只有 findAllRightAnswers() 函数,大概13行代码左右,看懂此函数就相当于整个题都懂了,代码如下:

// 定义一个Answer类来表示一个答案
data class Answer(val number: Int, val name: String)

// 定义一个Student类来表示一个同学
data class Student(val first: Answer, val second: Answer)

fun main() {
    // 定义A、B、C、D、E五个同学
    val A = Student(Answer(2, "陕西"), Answer(5, "甘肃"))
    val B = Student(Answer(2, "湖北"), Answer(4, "山东"))
    val C = Student(Answer(1, "山东"), Answer(5, "吉林"))
    val D = Student(Answer(3, "湖北"), Answer(4, "吉林"))
    val E = Student(Answer(2, "甘肃"), Answer(3, "陕西"))

    // 定义一个集合,保存未知正确答案的所有同学
    var unknownAnswerStudentList = mutableListOf(A, B, C, D, E)
    // 定义保存正确答案的集合
    val rightAnswerList = mutableListOf<Answer>()
    // 假设第一个人的第一个答案是正确答案,保存到正确答案集合中
    rightAnswerList.add(A.first)
    // 因为找到A同学的正确答案了,所以把A同学从集合中移除
    unknownAnswerStudentList.remove(A)
    // 找到所有正确答案
    findAllRightAnswers(unknownAnswerStudentList, rightAnswerList, unknownAnswerStudentList.size)

    if (rightAnswerList.size == 5) {
        println("第一次假设就找到了正确答案,如下:")
    } else {
        println("第一次假设找不到正确答案,第二次肯定是正确答案,如下:")
        // 没找齐5个答案,说明最初假设的答案是错的,则另一个必定是正确答案,重新找
        unknownAnswerStudentList = mutableListOf(A, B, C, D, E)
        rightAnswerList.clear()
        rightAnswerList.add(A.second) // 假设另一个为正确答案
        unknownAnswerStudentList.remove(A)
        findAllRightAnswers(unknownAnswerStudentList, rightAnswerList, unknownAnswerStudentList.size)
    }

    // 打印正确答案
    rightAnswerList.forEach(System.out::println)

}

fun findAllRightAnswers(unknownAnswerStudentList: MutableList<Student>, rightAnswerList: MutableList<Answer>, findCount: Int) {
    // 遍历每一个同学,使用iterator是因为在遍历集合的同时还要删除集合的元素。
    val iterator = unknownAnswerStudentList.iterator()

    while (iterator.hasNext()) {
        val student = iterator.next()

        // 通过正确答案找正确答案
        if (rightAnswerList.any { student.first.number == it.number || student.first.name == it.name }) {
            // 如果第一个答案的编号或名称与正确答案的相同,根据"每个编号只有一个人答对"说明这个是错误答案,则第二个答案可能是正确答案
            if (rightAnswerList.any { student.second.number == it.number || student.second.name == it.name }) {
                // 如果第二个答案的编号或名称与正确答案相同,说明这个也是错误答案,说明我们的假设是错误的,无解,退出循环
                break
            }

            rightAnswerList.add(student.second) // 找到了正确答案,此学生的第一个答案是错误答案,所以第二个答案是正确答案
            iterator.remove()                   // 此学生已找到正确答案,可以从集合中移除掉了
        } else if (rightAnswerList.any { student.second.number == it.number || student.second.name == it.name }) {
            // 如果第二个答案的编号或名称与正确答案的相同,根据"每个编号只有一个人答对"说明这个是错误答案,则第一个答案可能是正确答案
            if (rightAnswerList.any { student.first.number == it.number || student.first.name == it.name }) {
                // 如果第一个答案的编号或名称与正确答案相同,说明这个也是错误答案,说明我们的假设是错误的,无解,退出循环
                break
            }

            rightAnswerList.add(student.first) // 找到了正确答案,此学生的第二个答案是错误答案,所以第一个答案是正确答案
            iterator.remove()                  // 此学生已找到正确答案,可以从集合中移除掉了
        }
    }

    if (unknownAnswerStudentList.size > 0 && findCount - 1 > 0) {
        // 如果还有同学没找到正确答案,且没有超过查找次数,则递归循环去找
        // 设定查找次数是因为如果你假设的答案是错误的,则永远也找不齐5个正确答案造成死循环
        findAllRightAnswers(unknownAnswerStudentList, rightAnswerList, findCount - 1)
    }
}

运行结果如下:

第一次假设找不到正确答案,第二次肯定是正确答案,如下:
Answer(number=5, name=甘肃)
Answer(number=1, name=山东)
Answer(number=3, name=陕西)
Answer(number=2, name=湖北)
Answer(number=4, name=吉林)

我也是第一次看到这道题,刚看到题时确实觉得有点难,但是当把题目审清楚理解清楚之后就很简单了,写代码的时候再使用面向对象的方式去写就非常的简单,比如一个学生用Student表示,而答案是一个学生的属性;一个答案用Answer表示,编号和省份是Answer的属性,大家一定要经常面向对象去写代码,不然你可能虽然有思路了,但是总感觉代码一写就觉得怎么没那么简单了!那是因为你的代码是面向过程的,不是面向对象的。

这里给大家讲一下我对题目的理解:每个同学都有两个答案,且肯定有一个是对的,有一个是错的,而且这个同学的正确答案肯定在另一个同学里有错误答案,这样才能推出这个同学的正确答案,比如:

A同学 2-陕西,5-甘肃

E同学 2-甘肃,3-陕西

我们就只看这两个同学,假设A同学的2-陕西是正确答案,根据"每个编号只有一个同学答对"的规则,可知2-甘肃是错的,因为2号只会对应到一个正确的省份陕西,反过来,陕西对应到了2号,则3-陕西也是错误答案了,什么?E同学的两个答案都是错误的?没错,就是这样的,存在这种情况是因为我们随便假设了一个答案为真,出现一个同学两答案都不正确的话,说明我们的假设出错了,出现了无解,此时再假设另一个答案为正确答案肯定就是对的了,因此,细节就是,通过一个正确答案的编号或省份名称都可以找到错误的答案,然后通过错误答案另一个自然就是正确答案了。

说了这么多,简单总结一下思路就是:随便假设一个是正确答案,我们可以通过一个正确答案找到错误答案,通过此错误答案又得到另一个正确答案,再通过这个正确答案再找错误答案,一直循环找就行了 ,总体思路是很简单的,就是细节上代码逻辑容易写错,慢慢调试就好了,关键总体思路不能乱!

突然想到一点:A同学 2-陕西,5-甘肃,假设2-陕西是正确答案,那需不需要使用5-甘肃这个错误答案去寻找正确答案呢?是否存在别的同学也有5-甘肃这样的错误答案呢?如果有的话写起来相信也难倒不了大家了,这里我也不去多想了,不是重点。

最后再提醒一点,写代码一定要面向对象去写,很重要哦!

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门