Coursera普林斯顿大学算法下Week3:Baseball Elimination 棒球淘汰赛

任务连接:http://coursera.cs.princeton.edu/algs4/assignments/baseball.htmlhtml

本任务的重点在于理解题目的意思,理解了意思很好编码。java

这篇文章对题目意思讲解的很是好,我也是看的他的才明白的:https://blog.csdn.net/lxinyuelxy/article/details/78605333网络

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