华为oj初级题目——合唱队
2016-03-26 11:11
417 查看
原题:
原题说了知识点是循环,虽然也正确,但成功误导了大批像我一样的菜鸟们。此外图片中描述中的满足条件应该是
此题正确的解题思想是动态规划。
先说说我的错误思路吧,可能有很多人跟我的思路一样,却还没明白错在哪里。
我的(错误)思路:以第i(0<=i<=n)个人为中心,分别向两边遍历,剔除比上一个高的人,加起来得到需要出列的同学总人数。n次循环后保留最小值。
举个列子:假设同学的身高:17,18,19,10,20,19,18。(不要吐槽都是矮人)
很明显正确答案是1。只需要身高为10的同学出列即可,然而按照算法,会将17,18,19都出列。显然会得到错误的答案。
正确思路:以第i(0<=i<=n)个人为中心,分别从两边向i遍历,求出最长上升序列。
不就是换了个遍历方向吗?有什么不同。这就要从动态规划的基本要素说起了(其实没说,不要打我)。
动态规划的基本要素:1.最优子结构 2.重叠子问题 (不懂的自己解决吧,我就不献丑了)
饮水思源,我的代码思(源)路(码)是从这里来的。
针对变量的解释:n:总人数;a:身高数组;a1:从0到i的最长子序列长度;a2:从n-1到i的最长子序列长度。比如:a[3]=2,表示从0到3号人,满足身高要求的最长序列长度是2,即至少出列2人使得剩下的人满足身高要求。至于出列哪两个人,动态规划算法无法解决。
代码通过了华为oj的测试用例,但我不能保证代码的正确性,如果您看出了我的代码有错误之处,欢迎指正。
内层循环的解释:
放在底部是因为我太笨了,写给我自己看的。
以测试用例为例:
186 186 150 200 160 130 197 200
下面的分析步骤是按照人的思维来的,跟代码本质是一样的,区别在于代码是按步执行的,而人的思维具有跳跃性。
首先有了a数组,a1,a2数组初始化都为1。
先计算a1:
a[0]=186, a1[0]=1;不用说。
a[1]=186, a1[1]=1;找到满足a[i]>a[k]且a1[k]最大的那个k,显然没找到,保持默认值1。
a[2]=150, a1[2]=1;与上面相同的做法。
a[3]=200, a1[3]=2;找到满足条件的k值,k=2,那么a1[3]=a1[2]+1;
a[4]=160, a1[4]=2;
a[5]=130, a1[5]=1;
a[6]=197, a1[6]=3;
a[7]=200, a1[7]=4;
计算a2:
先计算a2[7]
a2[7]=1;
a2[6]=1;
a2[5]=1;
a2[4]=2;
a2[3]=3;
a2[2]=2;
a2[1]=3;
a2[0]=3;
原题说了知识点是循环,虽然也正确,但成功误导了大批像我一样的菜鸟们。此外图片中描述中的满足条件应该是
T1<T2<......<Ti-1<Ti>Ti+1>......>TK。
此题正确的解题思想是动态规划。
先说说我的错误思路吧,可能有很多人跟我的思路一样,却还没明白错在哪里。
我的(错误)思路:以第i(0<=i<=n)个人为中心,分别向两边遍历,剔除比上一个高的人,加起来得到需要出列的同学总人数。n次循环后保留最小值。
举个列子:假设同学的身高:17,18,19,10,20,19,18。(不要吐槽都是矮人)
很明显正确答案是1。只需要身高为10的同学出列即可,然而按照算法,会将17,18,19都出列。显然会得到错误的答案。
正确思路:以第i(0<=i<=n)个人为中心,分别从两边向i遍历,求出最长上升序列。
不就是换了个遍历方向吗?有什么不同。这就要从动态规划的基本要素说起了(其实没说,不要打我)。
动态规划的基本要素:1.最优子结构 2.重叠子问题 (不懂的自己解决吧,我就不献丑了)
饮水思源,我的代码思(源)路(码)是从这里来的。
针对变量的解释:n:总人数;a:身高数组;a1:从0到i的最长子序列长度;a2:从n-1到i的最长子序列长度。比如:a[3]=2,表示从0到3号人,满足身高要求的最长序列长度是2,即至少出列2人使得剩下的人满足身高要求。至于出列哪两个人,动态规划算法无法解决。
import java.util.Scanner; public class Chorus { public static void main(String[] args) { Scanner input = new Scanner(System.in); int n; int[] a, a1, a2; n = input.nextInt(); a = new int ; a1 = new int ; a2 = new int ; for (int i = 0; i < n; i++) { a[i] = input.nextInt(); } for (int i = 0; i < n; i++) { a1[i] = 1;// 初始值置为1,因为最小是1,即他自己 // 求出到i的最长子序列 // 外层循环好懂,内层循环我来稍稍解释一下,它的作用是找到i之前最长的子序列,但是要满足i的身高大于j的身高,a1[i]就等于那个最长子序列+1(自身) // 可能还不好懂,建议没懂的同学找张纸划一下 for (int j = 0; j < i; j++) { if (a[i] > a[j] && a1[i] < a1[j] + 1) { a1[i] = a1[j] + 1; } } } // 同理 for (int i = n - 1; i >= 0; i--) { a2[i] = 1; // 求出到i的最长子序列 for (int j = n - 1; j > i; j--) { if (a[i] > a[j] && a2[i] < a2[j] + 1) { a2[i] = a2[j] + 1; } } } int max = 0; for (int i = 0; i < n; i++) { // 注意第i个人被加了2次 if (max < a1[i] + a2[i] - 1) { max = a1[i] + a2[i] - 1; } } System.out.println(n - max); } }
代码通过了华为oj的测试用例,但我不能保证代码的正确性,如果您看出了我的代码有错误之处,欢迎指正。
8 186 186 150 200 160 130 197 200 4
内层循环的解释:
放在底部是因为我太笨了,写给我自己看的。
以测试用例为例:
186 186 150 200 160 130 197 200
下面的分析步骤是按照人的思维来的,跟代码本质是一样的,区别在于代码是按步执行的,而人的思维具有跳跃性。
首先有了a数组,a1,a2数组初始化都为1。
先计算a1:
a[0]=186, a1[0]=1;不用说。
a[1]=186, a1[1]=1;找到满足a[i]>a[k]且a1[k]最大的那个k,显然没找到,保持默认值1。
a[2]=150, a1[2]=1;与上面相同的做法。
a[3]=200, a1[3]=2;找到满足条件的k值,k=2,那么a1[3]=a1[2]+1;
a[4]=160, a1[4]=2;
a[5]=130, a1[5]=1;
a[6]=197, a1[6]=3;
a[7]=200, a1[7]=4;
计算a2:
先计算a2[7]
a2[7]=1;
a2[6]=1;
a2[5]=1;
a2[4]=2;
a2[3]=3;
a2[2]=2;
a2[1]=3;
a2[0]=3;
相关文章推荐
- 华为路由器密码恢复
- 文件遍历排序函数
- 华为交换机的后缀详解
- Lua 学习笔记之C API 遍历 Table实现代码
- C#遍历文件夹后上传文件夹中所有文件错误案例分析
- C#中遍历Hashtable的4种方法
- Erlang中遍历取出某个位置的最大值代码
- C++实现图的邻接矩阵存储和广度、深度优先遍历实例分析
- C++动态规划之最长公子序列实例
- C++实现图的邻接表存储和广度优先遍历实例分析
- C++动态规划之背包问题解决方法
- C++非递归队列实现二叉树的广度优先遍历
- php遍历目录方法小结
- 一个目录遍历函数
- php遍历删除整个目录及文件的方法
- PHP遍历文件夹与文件类及处理类用法实例
- PHP遍历XML文档所有节点的方法
- php中使用key,value,current,next和prev函数遍历数组的方法
- C#使用前序遍历、中序遍历和后序遍历打印二叉树的方法
- C#使用foreach遍历哈希表(hashtable)的方法