1. 程式人生 > >Coursera普林斯頓大學演算法下Week3:Baseball Elimination 棒球淘汰賽

Coursera普林斯頓大學演算法下Week3:Baseball Elimination 棒球淘汰賽

本任務的重點在於理解題目的意思,理解了意思很好編碼。

import java.util.HashMap;

import edu.princeton.cs.algs4.FlowEdge;
import edu.princeton.cs.algs4.FlowNetwork;
import edu.princeton.cs.algs4.FordFulkerson;
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.Queue;

public class BaseballElimination {
	private final int teamNum; // 球隊數量
	private final int[][] g;  // 最近待比對戰表
	private final String[] teamName; // 球隊隊名錶
	private int meachesTeamNum; // 在構建FlowNetwork時表示隊伍之間的比賽節點個數
	private int V; // 在構建FlowNetwork時表示節點個數
	private int flow; // 在構建FlowNetwork時表示flow值
	private HashMap<Integer, Integer> v2id; // 用於存放構建FlowNetwork時的網路下標與球隊ID的對應關係
	private HashMap<String, Team> teams; // 球隊雜湊表	
	
	private class Team // 球隊類
	{
		private final int id;
		private final int wins;
		private final int loss;
		private final int left;
		
		public Team(int id, int wins, int loss, int left)
		{
			this.id = id;
			this.wins = wins;
			this.loss = loss;
			this.left = left;
		}
	}
	
	// 根據指定的檔名初始化
	public BaseballElimination(String filename)  
	{
		if (filename == null)
			throw new java.lang.IllegalArgumentException("the file name is null");
		
		In in = new In(filename);
		teamNum = Integer.parseInt(in.readLine()); // 球隊數量
		teams = new HashMap<String, Team>(); // 球隊雜湊表
		g = new int[teamNum][teamNum]; // 最近待比對戰表
		teamName = new String[teamNum]; // 隊名錶
		v2id = new HashMap<Integer, Integer>(); // 球隊id與下標對照表
		
		int id = 0;
		while (in.hasNextLine())
		{
			String readLine = in.readLine().trim();   // 讀取當前行
			String[] tokens = readLine.split(" +");
			
			String key = tokens[0]; // 隊名
			int wins = Integer.parseInt(tokens[1]); 
			int loss = Integer.parseInt(tokens[2]);
			int left = Integer.parseInt(tokens[3]);
			
			if (!teams.containsKey(key)) 
			{
				teamName[id] = key;
				teams.put(key, new Team(id, wins, loss, left));
				
				for (int i = 0; i < teamNum; i++)   // 最近對戰
					g[id][i] = Integer.parseInt(tokens[4+i]);
			
				id++;
			}	
		}
	}
	
	// 球隊數量
	public int numberOfTeams()    
	{
		return teamNum;
	}
	
	// 球隊列表
	public Iterable<String> teams()
	{
		return teams.keySet();
	}
	
	// 檢驗隊名是否有效
	private void isValid(String team)
	{
		if (team == null) 
			throw new java.lang.IllegalArgumentException("the teamName is null");
		
		if (!teams.containsKey(team)) 
			throw new java.lang.IllegalArgumentException("the tameName is wrong");
	}
	
	// 給定球隊team的贏球數
	public int wins(String team)     
	{
		isValid(team); // 檢驗隊名是否有效
		
		return teams.get(team).wins;  // team球隊贏球數
	}
	
	// 給定球隊team的輸球數
	public int losses(String team)       
	{
		isValid(team); // 檢驗隊名是否有效
		
		return teams.get(team).loss;  // team球隊輸球數
	}
	
	// 給定球隊team的未比球數
	public int remaining(String team)        
	{
		isValid(team); // 檢驗隊名是否有效
		
		return teams.get(team).left;  // team球隊未比球數
	}
	
	// 球隊team1和team2之間的未比球數
	public int against(String team1, String team2)    
	{
		// 有效性檢查
		isValid(team1);
		isValid(team2);
		
		int id1 = teams.get(team1).id;   // 球隊team1的id
		int id2 = teams.get(team2).id;   // 球隊team2的id
		
		return g[id1][id2];
	}
	
	// 建立FlowNetwork類
	private FlowNetwork structureFlowNetwork(String team)
	{
		Team thisTeam = teams.get(team); // 當前球隊
		int thisId = thisTeam.id; // 當前球隊ID 
		int thisTeamMostWins = thisTeam.wins + thisTeam.left; // 當前球隊最大可獲勝數 
		
		meachesTeamNum = (teamNum - 1) * (teamNum -2) / 2; // 表示比賽的節點數
		V = meachesTeamNum + (teamNum - 1) + 2; // 圖中節點數
		flow = 0;
		
		int indexMeaches = 1; // 用於表示新增加的比賽節點的下標(1----meachesTeamNum+1)
		int indexI = meachesTeamNum; // 用於表示球隊節點i的下標(meachsTeamNum+1-------meachsTeamNum+teamNum)
		int indexJ = indexI;   // 用於表示球隊節點j的下標
		int s = 0; // 源節點s的下標
		int t = V-1; // 目標節點t的下標
		
		FlowNetwork flowNetwork = new FlowNetwork(V); // 構建flowNetwork類物件
	
		// i,j分別為Team的id
		for (int i = 0; i < teamNum; i++) 
		{
			if (i == thisId) // 關於thisID的球隊的flownetwork不會出現關於thisId的節點
				continue; 
			
			indexI++; // 球隊節點i的下標
			indexJ = indexI;  // 球隊節點j的下標
			
			if (thisTeamMostWins < wins(teamName[i])) // 情景1 thisID的球隊的最大可能獲勝數 < 球隊i的當前獲勝數 
				return null;
			
			for (int j = i + 1; j < teamNum; j++) 
			{
				if (j == thisId) // 關於thisID的球隊的flownetwork不會出現關於thisId的節點
					continue;
				
				indexJ++;  // 球隊節點j的下標
				flow = flow + g[i][j]; // 流
				flowNetwork.addEdge(new FlowEdge(s, indexMeaches, g[i][j])); // 新增關於比賽節點到源節點s的邊
				flowNetwork.addEdge(new FlowEdge(
						indexMeaches, indexI, Double.POSITIVE_INFINITY)); // 新增比賽節點到對應的球隊節點i之間的邊
				flowNetwork.addEdge(new FlowEdge(
						indexMeaches, indexJ, Double.POSITIVE_INFINITY)); // 新增比賽節點到對應的球隊節點j之間的邊
				
				indexMeaches++;
			}
			
			v2id.put(indexI, i); // 網路下標與球隊ID的對應關係
			flowNetwork.addEdge(new FlowEdge(
					indexI, t, thisTeamMostWins-wins(teamName[i]))); // 新增球隊節點i到目標節點s之間的邊
		}
		
		return flowNetwork;
	}
	
	// 球隊team是否在數學上被淘汰
	public boolean isEliminated(String team)      
	{
		isValid(team);
		FlowNetwork gFlowNetwork = structureFlowNetwork(team);
		if (gFlowNetwork == null)  // 情形1
			return true; 
		else     // 情形2
		{
			FordFulkerson fordFulkerson = new FordFulkerson(gFlowNetwork, 0, V-1);
			return flow > fordFulkerson.value();
		}	
	}
	
	// 消除給定團隊的子集R 如果未消除,則為空
	public Iterable<String> certificateOfElimination(String team)  
	{
		isValid(team);
		if (!isEliminated(team))   // team未被淘汰
			return null;
		else  // team被淘汰
		{
			Queue<String> certificates = new Queue<>();  // 存放被消除子集
			int thisId = teams.get(team).id;  // 當前球隊ID 
			FlowNetwork flowNetwork = structureFlowNetwork(team); // 構建關於team的flowNetwork
			
			if (flowNetwork == null) // 情景1 
			{
				int thisTeamMostWins = wins(team) + remaining(team); // 當前球隊最大可獲勝數 
				for (int i = 0; i < teamNum; i++) 
				{
					if (i == thisId) 
						continue;
				
					if (thisTeamMostWins < wins(teamName[i])) 
						certificates.enqueue(teamName[i]); // 入棧	
				}
			} 
			else // 情景2 
			{
				FordFulkerson fordFulkerson = new FordFulkerson(flowNetwork, 0, V-1);
				for (int i = 1 + meachesTeamNum; i < V; i++) // 球隊節點 
				{
					if (fordFulkerson.inCut(i)) 
					{
						int id = this.v2id.get(i); // 網路下標為i的球隊的id
						certificates.enqueue(teamName[id]);
					}
				}	
			}
			return certificates;
		}
	}
	
}