微软2016年4月实习生笔试第三题-Demo Day题解
来源:互联网 发布:中国社会发展数据库 编辑:程序博客网 时间:2024/06/01 10:09
这道题选择动态规划做,说实话,一开始看到动态规划,我是懵逼的,对于我等渣渣,还啥都没学呢。可是大神做出来了,为了赶上脚步,看懂他的代码,于是花了几天的时间,看动态规划。现在将所理解的动态规划加以总结:
我们常会遇到最优化决策问题,比如经典最长公共子序列(不同于公共子串),背包问题,斐波那契数列等等,它们的共同点是:1、都要求最优解;2、都有重复子问题;3、都有最优子结构。如果查百度百科或维基百科,都会有这些字眼,因为这是利用动态规划解决某一问题的原因。最优决策应该容易理解,我们要求的问题就是一个优化问题;重复子问题简单说就是,我们要解决的这个大问题,可以划分为需要重复解决的小问题,比如求x,y的最长公共子序列,我们可以求x[i]和y[j]的最长公共子序列,而对于每一个递增的i和j就是不断重复求解子问题的过程,直到x,y的长度N,M;而最优子结构呢,就是要每一个子问题都有最优解,这样,我们才能利用不断求子问题的最优解,来求整个大问题的最优解。
而当我们使用动态规划来解决问题的时候,我们需要一个基本的算法思路:我们之所以用动态规划来解决问题,是因为它的备忘法比传统记忆法要优越,即将每次计算子问题的最优解,用一个二维表记录下来c[i][j],其中行代表所处的子问题状态,列代表该状态下的最优解。这样,当进行下一状态计算的时候,如果需要前面已经计算过的值,只需要从备忘表中取出来即可,避免了重复运算。
以求x,y两序列的最长公共子序列为例:
LCS(x,y,i,j)if(x,y!=null){ then if(x[i]=y[j]){ c[i,j]=LCS(x,y,i-1,j-1)+1; }else { c[i,j]=max{LCS(x,y,i-1,j), LCS(x,y,i,j-1)}return c[i,j];以上为算法导论里求解最长公共子序列的伪代码。其中c[i][j]这个二维数组用来记录,x[i]和y[j]的LCS长度,初始值i=0,j=0时,c=0
因此,动态规划解题的算法复杂度为O(m*n)。但是我们需要注意,虽然动态规划的备忘法很好的解决了需要重复利用的值的问题,但是它是以牺牲空间为代价,来储存这些二维表的,如果所需计算的规模过于庞大,我们就要考虑空间溢出问题。对于一个可能与前N个阶段相关的问题,建立数组Data[0..N],其中各项为前面N个阶段的保存数据。这样不采用这种内存节约方式时对于阶段k的访问只要对应成对数组Data中下标为k mod (N+1)的单元的访问就可以了。这种处理方法对于程序修改的代码很少,速度几乎不受影响,而且需要保留不同的阶段数也都能很容易实现。
然后了解了动态规划的知识后,就可以来解这道题:
这道题实质是利用不断将empty改成blocked或将blocked改成empty,来改变机器人运行的方向,达到终点。其中的特殊情况为:起点终点、第一行第一列、最后一行最后一列;这些地方机器人的方向或改单元的障碍物为确定值,需要单独拿出来讨论。其余为中心部分。总体思路为:求到某一单元需要改变的单元数=min{其上单元备忘值,其左单元备忘值}+自身是否为障碍物(b则加1)
大神的代码
import java.util.Scanner;public class DemoDay{public int minChange(char[][] grids) { //传入图,b为障碍物,.为无障碍物,起点和终点默认为.int m = grids.length; //行数int n = grids[0].length; //列数 int[][] a = new int[m][n]; //记录从起点到达该单元所需修改的单元数目最小值 int[][] right = new int[m][n]; /*记录除起点、终点、最后一行、最后一列以外的单元的当前运动方向,0为向右,1为向下,2为任意方向 * (如果从上方和左方到达该单元所需改变的单元数目相同,则可从任意方向到达该单元)*/ //只有一行,返回所有障碍物数目 if(m==1){ int count = 0; for(int i=1;i<n;i++){ if(grids[0][i]=='b'){ count++; } } return count; } //只有一列,返回所有障碍物数目 if(n==1){ int count = 0; for(int i=1;i<m;i++){ if(grids[i][0]=='b'){ count++; } } return count; } //列数大于1且行数大于1 /* * 动态规划,一层一层遍历该图,每个单元遍历一次,时间复杂度为m*n * 每遍历到一个单元,计算到该单元所需改变的单元数目最小值 */ for(int i=0;i<m;i++){//一行一行记录每一状态的最优解,知道求到最终状态a[m][n] for(int j=0;j<n;j++){ //初始化起点 if(i==0 && j==0){ a[i][j] = 0; }else //初始化第一排(第一排的方向必向右),每一个状态只受前一状态的左侧状态影响和当前状态的影响 if(i==0){ a[i][j] = a[i][j-1] + (grids[i][j]=='b'?1:0); right[i][j] = 0; }else //初始化第一列(第一列的方向必向下) if(j==0){ //起点下面的一个单元(只有当起点右边的单元为1障碍物时,方向才会向下) if(i==1 && grids[i-1][j+1] != 'b'){ a[i][j] = 1;//将起点右侧一个单元从empty换位blocked,因为change grids是双向的,题目只要求最小转换数,并不是只算从blocked到empty的 }else{ //其他情况(方向必向下,不受第二列单元影响) a[i][j] = a[i-1][j] + (grids[i][j]=='b'?1:0); } right[i][j] = 1;//第一列的方向必为下 }else //终点(取上方和左方单元中最小的那个值) if(i==m-1 && j==n-1){ a[i][j] = Math.min(a[i-1][j], a[i][j-1]); }else //到达最后一行(最后一行的所有单元,其左边的单元无论方向向右还是向下,都会改变为向右),此时最后一行不包括第一列,因为前面已经讨论过了第一列,用else则把第一列的已经排除在外了 if(i==m-1){ int top = a[i-1][j]; int left = a[i][j-1]; //上方的单元方向向右,且右上方单元不为障碍物(此时需要改变上方单元的运动方向为向下,即需要修改右上角单元为障碍物) if(right[i-1][j]==0 && grids[i-1][j+1] != 'b'){ top++; } a[i][j] = Math.min(top, left) + (grids[i][j]=='b'?1:0); right[i][j] = 0; }else //到达最后一列(最后一列的所有单元,其上边的单元无论方向向右还是向下,都会改变为向下) if(j==n-1){ int top = a[i-1][j]; int left = a[i][j-1]; //左方的单元方向向下,且左下方单元不为障碍物(此时需要改变上方单元的运动方向为向右,即需要修改左下角单元为障碍物) if(right[i][j-1]==1 && grids[i+1][j-1] != 'b'){ left++; } a[i][j] = Math.min(top, left) + (grids[i][j]=='b'?1:0); right[i][j] = 1; }else{ //在图的中心部分时 int top = a[i-1][j]; int left = a[i][j-1]; //上方的单元方向向右,且右上方单元不为障碍物(此时需要改变上方单元的运动方向为向下,即需要修改右上角单元为障碍物) if(right[i-1][j]==0 && grids[i-1][j+1] != 'b'){ top++; } //左方的单元方向向下,且左下方单元不为障碍物(此时需要改变上方单元的运动方向为向右,即需要修改左下角单元为障碍物) if(right[i][j-1]==1 && grids[i+1][j-1] != 'b'){ left++; } if(top<left){ a[i][j] = top + (grids[i][j]=='b'?1:0); right[i][j] = 1; } else if(left<top){ a[i][j] = left + (grids[i][j]=='b'?1:0); right[i][j] = 0; }else{//如果两边一样活left<top,统一将方向设置为向右0,不可以,这样运行wr a[i][j] = left + (grids[i][j]=='b'?1:0); right[i][j] = 2; } } } } return a[m-1][n-1]; }public static void main(String []args){DemoDay demo = new DemoDay();Scanner in = new Scanner(System.in); while(in.hasNext()) {//判断一开始有没有输入,可以去掉 int N = in.nextInt(); int M = in.nextInt(); String delet = in.nextLine();//废弃的流 char[][] grid = new char[N][M]; for(int i=0;i<N;i++){//二维数组读入grids,一行一行读,每一行读的string的char为列元素 String line = in.nextLine(); for(int j=0;j<M;j++){ grid[i][j] = line.charAt(j); } } System.out.println(demo.minChange(grid)); }}}
- 微软2016年4月实习生笔试第三题-Demo Day题解
- 微软2016实习生笔试--第三题Demo Day
- 微软2016年4月实习生笔试第一题-font size题解
- hihocoder 1290 -- 微软2016校园招聘4月在线笔试-3-Demo Day
- 微软2016校园招聘4月在线笔试 hihocoder 1290 Demo Day (dp)
- Demo Day (微软2016校园招聘4月在线笔试)
- 微软笔试题3 Demo Day
- 微软笔试题3:Demo Day
- 微软2016校园招聘4月:hihocoder- #1290 : Demo Day
- 阿里2015年4月实习生招聘研发岗笔试题——RPC题解
- 微软笔试3 Demo Day
- 微软2012暑假实习生笔试题解析
- 微软2012暑假实习生笔试题解析
- 微软2013暑假实习生笔试题解析
- 2010年11月21日微软实习生笔试题--反序对 数目求解
- 2012年4月7日微软实习生笔试题目之一
- 微软2012暑期实习生校园招聘 软件测试笔试题(4月7号)
- 微软2016校园招聘9月在线笔试题解
- FCKeditor编辑器 中文乱码问题 彻底解决方案
- 总容量显示为8G
- iOS-地图真实坐标表示形式之间转换(double型,int型 互转)
- PHP+Mysql+jQuery实现文件下载次数统计
- .NET软件工程师48个面试题目
- 微软2016年4月实习生笔试第三题-Demo Day题解
- iOS各种最全的画图实现 (折线图.走势图.柱状图.动态曲线图.形状图.划线图)
- 线程与进程
- OpenCV3.1.0鱼眼相机标定及畸变校正
- 一篇不错的讲解Java异常的文章
- 注释转换(C---->C++)
- 基于linux 3.10.49内核 从dts文件里注册platform_device流程分析
- Android中四中声音设置
- 屏蔽Xcode编译警告