小猪吃米的面试题——解法比较

    技术2026-08-09  4

    这道题是我从网上找到的,据说是来自Exoweb,个人认为这是一个很好的公司

    在国际象棋的棋盘上面有 NxN 个格。每个格里面有若干的米粒。一只小猪站在 1x1 的格里,小猪每次只能向高位的列或行移动。小猪会吃掉所经过的格子里面所有的米粒。请编写程序计算小猪能吃掉的米粒的最大值

    需要说明的是,我看题的时候和后来去解题的中间隔了一段时间,一个不小心把故事的主人公搞错了,以至于我写的类是Chook(鸡),我也更加认为这道题概括为小鸡吃米 比较合适

    还是那句话,欢迎拍砖,请大侠多多指教

    上代码

    class Chook1 : ChookBase { /// <summary> /// 我其实一开始就想到的是2A的方法,但是像总的找一种的别的办法来对比效率,所以就想到了用最慢的穷举法 /// 不过后来发现穷举也不是像数数一样不懂脑子就可以搞出来的,还是稍微费了点时间,看来还是能力有待提高 /// </summary> int length = 0; int[][] arrInt; int max = 0; public override void work() { eat(0, 0, arrInt[0][0]); getMax(); } /// <summary> /// 把小鸡放到这个格子桌面上(获取桌面的参数) /// </summary> /// <param name="length">边长</param> public Chook1(int length, int[][] arrInt) { this.length = length; this.arrInt = arrInt; } private void getMax() { Console.WriteLine("the maximum food: " + max); } private void eat(int x, int y, int foodin) { /*** * 从arrInt[0][0]向xy增大方向遍历 * 主要思想就是穷举,然后找到最大的max * 个人认为这是大众的想法,而不是程序员的想法 **/ int food; if (x == y && x == length - 1) { if (max < foodin) max = foodin; } else { if (x < length - 1) { food = foodin + arrInt[x + 1][y]; eat(x + 1, y, food); } if (y < length - 1) { food = foodin + arrInt[x][y + 1]; eat(x, y + 1, food); } } } 

     

    class Chook2A : ChookBase { /** * 这个思路源自于小学的数学竞赛题目 * 一只蚂蚁从一个m*n的格子的左下角向右上角前进,每次只能走一步,向上或者是向右,求一共有多少种走法 * 具体思路将起来比较麻烦,我做一张图,请看下方的那张白底的图即可(我居然是用QQ做的图。。。) * 图中的思路很简单,从a到b,沿着边路的全部为1(不论是到边路的哪个点,只有一种走法),其余的点,都是左方和下方数字的和(图中是临时的示范,算错了不要介意) * 而这道题目也可以相似的解决,我说一下不同之处 * 1:直观上讲,图中的是点到点,点与点之间的线段是数值(都是1),而本题是格子到格子,每个格子代表一个数值,如果不熟悉,建议从新作图进行演示 * 2:需要比较大小 * 3:故事背景不一样(小鸡吃米 PK 蚂蚁爬行,其实也没啥不一样的) */ int length = 0; int[][] arrInt; int[][] arrSum; public override void work() { eat(); getMax(); } public Chook2A(int length, int[][] arrInt) { this.length = length; this.arrInt = arrInt; arrSum = new int[length][]; for (int i = 0; i < length; i++) { arrSum[i] = new int[length]; } } private void getMax() { Console.WriteLine("the maximum food: " + arrSum[length - 1][length - 1]); } /// <summary> /// 像那道小学数学竞赛题一样,从a到b,选择一定的顺序,加过去,就可以了 /// 这个方法的好处是, 只要获得了某一个点的最大值,可以不需要再利用其他之前用的值进行计算 /// 如果要说时间复杂度的话,我认为应该是O /// </summary> private void eat() { arrSum[0][0] = arrInt[0][0]; for (int j = 1; j < length; j++) { arrSum[0][j] = arrInt[0][j] + arrSum[0][j - 1]; } for (int i = 1; i < length; i++) { arrSum[i][0] = arrInt[i][0] + arrSum[i - 1][0]; for (int j = 1; j < length; j++) { arrSum[i][j] = Math.Max(arrSum[i - 1][j], arrSum[i][j - 1]) + arrInt[i][j]; } } } }

     

    class Chook2B : ChookBase { /** * 这是我网上看到这道题目是楼主的解法,个人认为这才是程序员的解法,虽然效率没有2A高,但是思路比较简洁(方法eatAny(int x, int y)注释) * 贴中说是用了“动态规划”,其实我之前对这个词并没有什么印象 * 先贴一下网上的概述“把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法” * 我也还没细看,准备过会儿慢慢看,不清楚的大家可以自己查看 * 以下方法其实也不是很复杂,稍微有一点耐心即可 */ int length = 0; int[][] arrInt; int[][] arrSum; public override void work() { eat(); getMax(); } public Chook2B(int length, int[][] arrInt) { this.length = length; this.arrInt = arrInt; arrSum = new int[length][]; for (int i = 0; i < length; i++) { arrSum[i] = new int[length]; } } private void getMax() { Console.WriteLine("the maximum food: " + arrSum[length - 1][length - 1]); } private void eat() { eatAny(length - 1, length - 1); } /// <summary> /// 要获取从(0, 0)到(x, y)的最大值,只需要知道(0, 0)到(x - 1, y)和(x, y - 1)的最大值即可 /// 选取两者之间的最大值,加上(x, y)点的值,即可 /// 其实这个思路在2A中也有体现 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> private int eatAny(int x, int y) { if (x == 0 && y == 0) { return arrInt[0][0]; } int sum1, sum2; sum1 = (x > 0 ? eatAny(x - 1, y) : 0); sum2 = (y > 0 ? eatAny(x, y - 1) : 0); arrSum[x][y] = arrInt[x][y] + Math.Max(sum1, sum2); return arrSum[x][y]; } }

    现在来看一下时间效率的对比

    其实我一开始以为使用动态规划法应该也是线性的时间复杂度,而且和2A也有相似的思想,所以我才取名2B的,实在不好意,(最后一种方法的作者也说了是O2,但是我没相信),不过结果出来后发现我错了,不过仔细想想也确实是O2。

     

    最后上一下主函数

    static void Main(string[] args) { int n = 15;//n*n的矩阵 IntArray ia = new IntArray(n); int[][] intArray = new int[n][]; for (int i = 0; i < n; i++) { intArray[i] = new int[n]; } ia.getIntArray(intArray); ia.showIntArray(intArray); List<ChookBase> cList = new List<ChookBase>(); cList.Add(new Chook1(n, intArray)); cList.Add(new Chook2A(n, intArray)); cList.Add(new Chook2B(n, intArray)); foreach(ChookBase c in cList) { StopWatch sw = new StopWatch();//用于计算时间 sw.setStart(); c.work(); sw.setEnd(); Console.WriteLine(c.GetType().Name + ":" + sw.getTime()); } Console.ReadLine(); }

    至于产生随机数和计算时间的类我就不贴了,需要可以找我要或者看前面几篇博客

    最新回复(0)