Google-APAC2015-Addition (1)
2014-10-23 07:52
344 查看
一个在求职道路上的伪·程序猿之独白:
已经毕业快半年了,每每找工作面试的时候总会收到好人卡,现在很怕这种以"Thank you"开头的email。
之前会觉得说,自己已经把leetcode题目刷完了几遍了,cc的主要章节也刷了两遍了,为什么总是过不了on site这一面呢。后来才深刻明白Parker大神所说:并非过了test case就表明你的算法正确。现在终于明白这么一点,哪怕算法是bug free的,可能思路是不好的,过于臃肿;也可能是数据结构定义得不好,导致算法很复杂。无论怎么说,都是”缺乏经验,或者太弱“的表现。
之前还会觉得说,明明自己花了很多时间在coding上面,又是学Andrew Ng的machine learning,又是学developing a scalable app,又是学computer graphics,总之每天都很忙很累,却不见自己在找工作方面有收获。最后被鸡K、G神、天天等众位大神指出:好钢用在刀刃上。我是太过于分散自己的精力,学东西只是学皮毛学得泛,因此什么都学不好。
于是我决定先把 “ 找工作这个第一要务 ” 完成,为此,我决定认真完成两件事情:算法 + java基础知识(上次Yahoo! on site的时候就被三哥问了serialization的问题没有答上来,觉得是java的基础知识储备不足)。算法关注三件事情:leetcode + cc + google_apac ; java基础知识目前关注:马士兵老师的java视频 + 面试网站的题库。
为了克服自己的惰性,决定开始写blog来记录学习过程,不知道能够坚持多久呢。大神们求轻拍,求传授经验,要是有北美的refer也跪求,鄙人邮箱:sssjtuhuang@gmail.com。
========================================================================
先来看第一题,这道题目我先说第一次解small的test case的思路,这个思路有bug,但可以应付小的test case。
原题:google-APAC-2015-addition
Step 1 : 题目理解
直接看test case:
test case的输入有两部分,一部分是题库,比如:
a+b=3
b+c=3
c+d=3
另一部分是问题,比如:
a+c
a+d
b+c
b+d
要求用题库用来求问题。如果有解就要输出,如果无法求解就不输出,比如上面的test case输出就是:
a+d=3
b+c=3
========================================================================
Step 2: 算法
题目是一道传统数学题,可以用高斯消元来解,但是从程序角度而言,要构建一个矩阵的类感觉比较麻烦。
题目的思想是“深搜”,比如想求解a+d,可以通过(a+b)+(c+d),然后转换为b+c来求解,而b+c是已知的,所以可以知道a+d的答案。
曾经想过使用stack来维持这种“搜索”,后来发现逻辑太过混乱。Parker大神一语道破:实质上a+b可以看作一条以a, b为端点的边,边的weight就是a+b的值,要求解a + d只要找到一条从a开始从d结束的路径就可以了。但是还要满足另外一个条件就是,路径所包含的边的数目要为奇数条。
Parker大神指出这么一个test case:
如果已知题目是这样:
a+b=1
b+c=1
c+a=1
d+e=1
e+f=1
f+e=1
要求解a+f,这是找到一条从a到f的路径,但是a,b,c,d,e,f都是可以分别求解出来的。所以上面这个算法还要多增加一个逻辑判断,就是如果a到f没有路径,就转化为求a+a和f+f,然后除以2,如果a+a,和f+f两个都有解,a+f才有解,如果其中一个无解都不行。
========================================================================
Step 3: 数据结构
Edge类,包括String p1, String p2, int weight
一个Set用来存所有已知的edge,这是一个global variable
dfs的时候使用一个Set来存visited的edge(防止形成死循环),另外一个ArrayList用来存path。
========================================================================
Step 4: 细节
#1 @Override equals(Object obj)
#2 写Edge的hashCode的时候一开始使用了:
hash = 31 * hash + p1.hashCode();
hash = 31 * hash + p2.hashCode();
这个hashCode和equals方法不一致,在equals方法里面,判断是(this.p1==obj.p1 && this.p2==obj.p2) || (this.p1==obj.p2 && this.p2 == obj.p1) [这里语法不谨慎,理解意思就好]。这样如果两个对象equals了,反而得到的hashCode不一样!因此把hashCode方法写成这样就好:
hash = p1.hashCode() + p2.hashCode();
这样可以保证对称性。
========================================================================
Step 5: 代码
已经毕业快半年了,每每找工作面试的时候总会收到好人卡,现在很怕这种以"Thank you"开头的email。
之前会觉得说,自己已经把leetcode题目刷完了几遍了,cc的主要章节也刷了两遍了,为什么总是过不了on site这一面呢。后来才深刻明白Parker大神所说:并非过了test case就表明你的算法正确。现在终于明白这么一点,哪怕算法是bug free的,可能思路是不好的,过于臃肿;也可能是数据结构定义得不好,导致算法很复杂。无论怎么说,都是”缺乏经验,或者太弱“的表现。
之前还会觉得说,明明自己花了很多时间在coding上面,又是学Andrew Ng的machine learning,又是学developing a scalable app,又是学computer graphics,总之每天都很忙很累,却不见自己在找工作方面有收获。最后被鸡K、G神、天天等众位大神指出:好钢用在刀刃上。我是太过于分散自己的精力,学东西只是学皮毛学得泛,因此什么都学不好。
于是我决定先把 “ 找工作这个第一要务 ” 完成,为此,我决定认真完成两件事情:算法 + java基础知识(上次Yahoo! on site的时候就被三哥问了serialization的问题没有答上来,觉得是java的基础知识储备不足)。算法关注三件事情:leetcode + cc + google_apac ; java基础知识目前关注:马士兵老师的java视频 + 面试网站的题库。
为了克服自己的惰性,决定开始写blog来记录学习过程,不知道能够坚持多久呢。大神们求轻拍,求传授经验,要是有北美的refer也跪求,鄙人邮箱:sssjtuhuang@gmail.com。
========================================================================
先来看第一题,这道题目我先说第一次解small的test case的思路,这个思路有bug,但可以应付小的test case。
原题:google-APAC-2015-addition
Step 1 : 题目理解
直接看test case:
test case的输入有两部分,一部分是题库,比如:
a+b=3
b+c=3
c+d=3
另一部分是问题,比如:
a+c
a+d
b+c
b+d
要求用题库用来求问题。如果有解就要输出,如果无法求解就不输出,比如上面的test case输出就是:
a+d=3
b+c=3
========================================================================
Step 2: 算法
题目是一道传统数学题,可以用高斯消元来解,但是从程序角度而言,要构建一个矩阵的类感觉比较麻烦。
题目的思想是“深搜”,比如想求解a+d,可以通过(a+b)+(c+d),然后转换为b+c来求解,而b+c是已知的,所以可以知道a+d的答案。
曾经想过使用stack来维持这种“搜索”,后来发现逻辑太过混乱。Parker大神一语道破:实质上a+b可以看作一条以a, b为端点的边,边的weight就是a+b的值,要求解a + d只要找到一条从a开始从d结束的路径就可以了。但是还要满足另外一个条件就是,路径所包含的边的数目要为奇数条。
Parker大神指出这么一个test case:
如果已知题目是这样:
a+b=1
b+c=1
c+a=1
d+e=1
e+f=1
f+e=1
要求解a+f,这是找到一条从a到f的路径,但是a,b,c,d,e,f都是可以分别求解出来的。所以上面这个算法还要多增加一个逻辑判断,就是如果a到f没有路径,就转化为求a+a和f+f,然后除以2,如果a+a,和f+f两个都有解,a+f才有解,如果其中一个无解都不行。
========================================================================
Step 3: 数据结构
Edge类,包括String p1, String p2, int weight
一个Set用来存所有已知的edge,这是一个global variable
dfs的时候使用一个Set来存visited的edge(防止形成死循环),另外一个ArrayList用来存path。
========================================================================
Step 4: 细节
#1 @Override equals(Object obj)
#2 写Edge的hashCode的时候一开始使用了:
hash = 31 * hash + p1.hashCode();
hash = 31 * hash + p2.hashCode();
这个hashCode和equals方法不一致,在equals方法里面,判断是(this.p1==obj.p1 && this.p2==obj.p2) || (this.p1==obj.p2 && this.p2 == obj.p1) [这里语法不谨慎,理解意思就好]。这样如果两个对象equals了,反而得到的hashCode不一样!因此把hashCode方法写成这样就好:
hash = p1.hashCode() + p2.hashCode();
这样可以保证对称性。
========================================================================
Step 5: 代码
import java.util.*; import java.io.*; public class Solution { // global variable to store the answer to questions and visited edges private static Set<Edge> dict = new HashSet<Edge>(); public static void main(String[] args) { File inFile = new File ("C-small-practice.in"); File outFile = new File ("C-small-practice.out"); try { BufferedReader br = new BufferedReader(new FileReader(inFile)); BufferedWriter bw = new BufferedWriter(new FileWriter(outFile)); int T = Integer.parseInt(br.readLine()); for (int i = 1; i <= T; i++) { dict.clear(); int N = Integer.parseInt(br.readLine()); for (int j = 0; j < N; j++) { String s = br.readLine(); String[] parts = s.split("[\\+\\=]"); String p1 = parts[0]; String p2 = parts[1]; int wt = Integer.parseInt(parts[2]); Edge e = new Edge(p1, p2, wt); dict.add(e); } int Q = Integer.parseInt(br.readLine()); bw.write("Case #" + i + ":\n"); for (int j = 0; j < Q; j++) { String s = br.readLine(); String[] parts = s.split("[\\+]"); String src = parts[0]; String dst = parts[1]; // begin searching the path ArrayList<Edge> path = new ArrayList<Edge>(); HashSet<Edge> visited = new HashSet<Edge>(); if (dfs(src, dst, path, visited)) { // create new edge and put it in the dictionary int v = val(path); Edge e = new Edge(src, dst, v); dict.add(e); bw.write(src + "+" + dst + "=" + v + "\n"); } else { ArrayList<Edge> path1 = new ArrayList<Edge>(); ArrayList<Edge> path2 = new ArrayList<Edge>(); HashSet<Edge> visited1 = new HashSet<Edge>(); HashSet<Edge> visited2 = new HashSet<Edge>(); boolean b1 = dfs(src, src, path1, visited1); boolean b2 = dfs(dst, dst, path2, visited2); if (b1 && b2) { int v1 = val(path1); int v2 = val(path2); // create new edge and put it in the dictionary Edge e1 = new Edge(src, src, v1); dict.add(e1); Edge e2 = new Edge(dst, dst, v2); dict.add(e2); Edge e = new Edge(src, dst, (v1+v2)/2); dict.add(e); bw.write(src + "+" + dst + "=" + (v1+v2)/2 + "\n"); } } } } bw.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } /* find a path from source(src) to destination(dst) */ /* edge case: e+e, src==dst */ public static boolean dfs (String src, String dst, ArrayList<Edge> path, Set<Edge> visited) { if (src.equals(dst) && path.size() % 2 == 1) return true; // find the path for (Edge e : dict) { if (visited.contains(e)) continue; // skip the duplicate edge String p = e.contains(src); if (p != null) { path.add(e); visited.add(e); if (dfs(p, dst, path, visited)) return true; path.remove(path.size()-1); // undo the edge visited.remove(e); } } return false; } /* calculate the value from source to destination */ public static int val (ArrayList<Edge> path) { int value = 0; for (int i = 0; i < path.size(); i++) { int wt = path.get(i).wt; if (i % 2 == 0) value += wt; else value -= wt; } return value; } } /* Edge class */ class Edge { String p1; String p2; int wt; // cons 9f35 tructor public Edge (String p1, String p2, int wt) { this.p1 = p1; this.p2 = p2; this.wt = wt; } // equals method @Override public boolean equals(Object obj) { if (obj == null) return false; if (this == obj) return true; if (this.getClass() != obj.getClass()) return false; final Edge e = (Edge) obj; return (p1.equals(e.p1) && p2.equals(e.p2)) || (p2.equals(e.p1) && p1.equals(e.p2)); } // hash code @Override public int hashCode() { return p1.hashCode() + p2.hashCode(); } // toString method @Override public String toString() { return p1 + " -> " + p2 + " : " + wt; } // if the edge contains one point, return the other one, else return null public String contains(String p) { if (p1.equals(p)) return p2; else if (p2.equals(p)) return p1; else return null; } }
相关文章推荐
- Google 2015 APAC Round A Problem C - Addition
- Google-APAC2015-Addition (2)
- Google APAC test 2015 Round B Problem A - Password Attacker
- Google APAC test 2015 Round B Problem B - New Years Eve
- Catalan数和应用 & 2015 google APAC round 2 problem D 括号配对问题
- Google中国2015校园招聘笔试Round D APAC Test Problem C. Sort a scrambled itinerary
- Google APAC test 2015 Round B Problem C - Card Game
- [ Google APAC 2015 University Graduates Test ] Round C APAC Test
- Google-APAC2015-"super 2048"
- Google APAC test 2015 Round B Problem D-Parentheses Order
- Google-APAC2015-"cut tiles"
- [Google APAC 2015]Round A.Problem B.Super 2048
- Google-APAC2015-"Seven-segment display"
- Google-APAC2015-"Password Attacker"
- Google-APAC2015-"New Years Eve"
- Google中国2015校园招聘笔试Round D APAC Test Problem D. Itz Chess
- Google中国2015校园招聘笔试Round D APAC Test Problem A. Cube IV
- Google中国2015校园招聘笔试Round D APAC Test Problem B. GBus count
- CODE JAM APAC 2015 round B: Problem A. Password Attacker(小球放进盒子的计数问题)
- Google I/O 2015 为 Android 开发者带来了哪些福利?