Google-APAC2015-Addition (1)

一个在求职道路上的伪·程序猿之独白:


已经毕业快半年了,每每找工作面试的时候总会收到好人卡,现在很怕这种以"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;

	// constructor
	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;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值