Google-APAC2015-Addition (2)

Parker大神说,addition这种类型的题目在ACM里面算是水题,学弱心里不禁深深颤抖了一下,果然ACM太难。突然想起了高中竞赛时代从来没有碰过的《难题集萃》。HAHA。

=======================================================================

Anyway,这里继续讨论addition这道题目。


#1 

上一篇blog给出来的解法是有bug的,Parker大神指出,我的问题出现在visted上面。他给出了以下test case:

a+b=1
b+c=1
a+d=1
d+e=1
e+c=1

求解a+c,如果是以下链路a->b->c,偶数条,不能求解,继续找a->b->c->e->d->a这个时候,所有的edge都visited过了,但是依然没有解。那么就返回无解麽?事实上不是,因为这样一条链路是有解的:a->b->c->e->d->a->b->c,奇数条。Parker大神指出,应该看点的奇偶性,有可能经过同一个点,比如说b,第一次出现的时候,是在链路的第2个位置,认为它是偶数点,第二次出现的时候是在链路的第7个位置,因此认为是奇数点,这两个点虽然都是同一个string,但是奇偶性不一样,故认为是不同点。故visited里面应该用来存的是点,而且点是有奇偶性的点。

另外一个值得注意的地方是,visited set在每次 dfs 失败之后不应该撤销搜索点。因为证明改点无法链接到目标点。之前因为撤销了失败搜索的点,而large test case里面的每条搜索路径又特别长,使得每次搜索耗时特别长。


=======================================================================

#2 Parker大神还给出来一个良好的数据结构:

每次Parse完(e.g. banana + apple = 4)的时候应该把string hash到int,比如banana->0, apple->1,这样每次判断端点的时候不需要处理字符串而是直接处理int,效率高很多!
存所有edge的时候使用list<list<int>>的结构去存,list.get(i)就可以得到一个list,这个list里面存着所有以 “ i ” 开始的边。

但是我觉得写起来特别费劲就没修改代码了。


=======================================================================

#3 数据结构

增加了一个point类,有parity和string两个成员;

 使用map<string, map<string, integer>>去存储边,第一个string是起点,第二个string是端点,这样在 dfs 扩展当前状态的时候,效率高很多。

使用set<Point>去存访问过的点。


=======================================================================

#4 代码

import java.util.*;
import java.io.*;

public class Solution {
	// global variable
	private static Map<String, HashMap<String, Integer>> edge_map = new HashMap<String, HashMap<String, Integer>>();
	
	public static void main(String[] args) {
		File inFile = new File ("C-large-practice.in");
		File outFile = new File ("C-large-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++) {
				// reset data
				edge_map.clear();

				int N = Integer.parseInt(br.readLine());
				for (int j = 0; j < N; j++) {
					String s = br.readLine();
					// parse line
					String[] parts = s.split("[\\+\\=]");
					String p1 = parts[0];
					String p2 = parts[1];
					int wt = Integer.parseInt(parts[2]);
					// update data
					if (!edge_map.containsKey(p1)) edge_map.put(p1, new HashMap<String, Integer>()); 
					if (!edge_map.containsKey(p2)) edge_map.put(p2, new HashMap<String, Integer>());
					edge_map.get(p1).put(p2, wt);
					edge_map.get(p2).put(p1, wt);
				}

				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<Integer> path = new ArrayList<Integer>();
					HashSet<Point> visited = new HashSet<Point>();
					if (dfs(src, dst, path, visited)) {
						int v = val(path);
						bw.write(src + "+" + dst + "=" + v + "\n");
						// update edge_map to avoid duplicate calculation
						edge_map.get(src).put(dst, v);
						edge_map.get(dst).put(src, v);
					} 
					else {
						ArrayList<Integer> path1 = new ArrayList<Integer>();
						ArrayList<Integer> path2 = new ArrayList<Integer>();
						HashSet<Point> visited1 = new HashSet<Point>();
						HashSet<Point> visited2 = new HashSet<Point>();
						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);
							bw.write(src + "+" + dst + "=" + (v1+v2)/2 + "\n");
							// update edge_map to avoid duplicate calculation
							edge_map.get(src).put(src, v1);
							edge_map.get(dst).put(dst, v2);
							edge_map.get(src).put(dst, (v1+v2)/2);
							edge_map.get(dst).put(src, (v1+v2)/2);
						}
					}
				}
			}
			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<Integer> path, HashSet<Point> visited) {
		if (!edge_map.containsKey(src) || !edge_map.containsKey(dst)) return false;
	
		if (edge_map.get(src).containsKey(dst) && path.size() % 2 == 0) {
			int wt = edge_map.get(src).get(dst);
			path.add(wt);
			return true;
		}
		
		if (visited.size() == 0) visited.add(new Point(src, 1)); // include the very first point in the visited set
		for (String s : edge_map.get(src).keySet()) {
			Point p = new Point(s, path.size() % 2);
			if (visited.contains(p)) continue; 
			visited.add(p);
			int wt = edge_map.get(src).get(s);
			path.add(wt);
			if (dfs(s, dst, path, visited)) return true;
			// undo the path and visited set
			path.remove(path.size()-1);
		}
		return false;
	}

	/* calculate the value from source to destination */
	public static int val (ArrayList<Integer> path) {
		int value = 0;
		for (int i = 0; i < path.size(); i++) {
			int wt = path.get(i);
			if (i % 2 == 0) value += wt;
			else value -= wt;
		}
		return value;
	}
}


/* Point class */
class Point {
	String s;
	int par; // 0 means even, 1 means odd

	// constructor
	public Point(String s, int par) {
		this.s = s;
		this.par = par;
	}

	// equals method
	public boolean equals(Object obj) {
		if (obj == null) return false;
		if (this == obj) return true;
		if (this.getClass() != obj.getClass()) return false;
		final Point p = (Point) obj;
		return (this.s.equals(p.s)) && (this.par == p.par);
	}

	// hashCode
	public int hashCode() {
		int hash = 1;
		hash = 31 * hash + s.hashCode();
		hash = 31 * hash + par;
		return hash;
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值