Timus 1837. Isenbaev's Number 要求计算 Isenbaev 数。
1837. Isenbaev's Number
Memory Limit: 64 MB
Input
Output
Sample
input | output |
---|---|
7 Isenbaev Oparin Toropov Ayzenshteyn Oparin Samsonov Ayzenshteyn Chevdar Samsonov Fominykh Isenbaev Oparin Dublennykh Fominykh Ivankov Burmistrov Dublennykh Kurpilyanskiy Cormen Leiserson Rivest |
Ayzenshteyn 2 Burmistrov 3 Chevdar 3 Cormen undefined Dublennykh 2 Fominykh 1 Isenbaev 0 Ivankov 2 Kurpilyanskiy 3 Leiserson undefined Oparin 1 Rivest undefined Samsonov 2 Toropov 1 |
Problem Source: Ural Championship 2011
Tags: graph theory
题意
这道题目是说俄罗斯乌拉尔大学有一个叫 Isenbaev 的牛人,他是 TopCoder Open 2009 和 ACM IPCP 2009 竞赛的双料冠军。许多人都以和他一同组队参赛为荣。退而其次,和他的队友一同参赛也是很有面子的。我们定义 Isenbaev 数如下:Isenbaev 他自己是 0,他的队友是 1。如果不是 Isenbaev 的队友,而是 Isenbaev 的队友参加的团队的成员,则是 2。以此类推。给出一些参赛队伍的名单,要求计算出这些队伍中每个人的 Isenbaev 数。
解答
下面就解答这道题目的是 C# 语言源程序:
using System; using System.Collections.Generic; // http://acm.timus.ru/problem.aspx?space=1&num=1837 static class Timus { enum State { Initial, Prepare, Ready, Done }; static readonly string VIP = "Isenbaev"; static SortedDictionary<string, int> ranks; static string[][] teams; static State[] states; static void Main() { Initialize(); Compute(); foreach (var kvp in ranks) Console.WriteLine(kvp.Key + " " + ((kvp.Value < 0) ? "undefined" : kvp.Value.ToString())); } static void Initialize() { ranks = new SortedDictionary<string, int>(); teams = new string[int.Parse(Console.ReadLine())][]; states = new State[teams.Length]; // value is: Initial for (var i = 0; i < teams.Length; i++) foreach (var name in teams[i] = Console.ReadLine().Split()) { ranks[name] = (name == VIP) ? 0 : -1; if (name == VIP) states[i] = State.Ready; } } static void Compute() { for (var rank = 1; ; rank++) { var done = true; for (var i = 0; i < states.Length; i++) if (states[i] == State.Ready) { foreach (var name in teams[i]) Compute(name, rank); states[i] = State.Done; done = false; } if (done) break; states.Replace(State.Prepare, State.Ready); } } static void Compute(string name, int rank) { if (ranks[name] >= 0) return; ranks[name] = rank; for (var i = 0; i < states.Length; i++) if (states[i] == State.Initial) foreach (var name2 in teams[i]) if (name2 == name) states[i] = State.Prepare; } static void Replace<T>(this IList<T> c, T a, T b) { for (var i = 0; i < c.Count; i++) if (EqualityComparer<T>.Default.Equals(c[i], a)) c[i] = b; } }
上述程序按照 Isenbave 数从小到大层次递进求解,分析如下:
- line 10: 静态字段 ranks 表示各人的 Isenbave 数,其类型是 SortedDictionay,键就是人的姓名,值是 Isenbave 数。
- line 11: 静态字段 teams 表示各参赛队伍,其类型是二维锯齿形数组。因为每个队伍的人数是不固定的。(题目中每个队伍均为三人,我们的程序不受这个限制)
- line 12: 静态字段 states 表示求解过程中各参赛队伍的状态,计有“初始、预备、就绪、完毕”四种。请参见第 7 行的 State 枚举类型。
- line 16 - 19: 首先调用 Initialize 方法读入数据并进行初始化,然后调用 Compute 方法求解,最后输出结果。
- line 22 - 33: Initialize 方法。首先初始化上面提到的三个字段,注意 states 数组各元素的值被默认初始化为 State.Initial。然后循环读入各参赛队伍的数据,并且根据读入的人员是否为 Isenbave 进行相应处理。如果某个参赛队伍中有 Isenbave,该队伍的状态就设置为 State.Ready。
- line 35 - 50: Compute 方法。从 1 开始递增 Isenbave 数进行求解,遍历各个参赛队伍,如果某个队伍的状态为 State.Ready 就遍历该队伍中的每个成员,调用下面要讲解的重载的 Compute 方法进行处理,处理后的队伍的状态改为 State.Done。最后,将处理后的状态为 State.Prepare 的队伍的都改为 State.Ready 状态,以便下次循环时继续求解。
- line 52 - 60: 重载的 Compute 方法。如果队伍中某人的 Isenbave 数已经求出来了,就什么也不做立即返回。否则,设置他的 Isenbave 数。然后遍历状态为 State.Initial 的队伍,如果某人是该队伍的成员,则这个队伍的状态设置为 State.Prepare,以便在下个层次的循环中进行处理。
- line 62 - 66: Replace 扩展方法。该方法用于对列表中的特定元素进行替换。可以作为一个小小的工具使用。
另一种解法
使用 List 代替数组的另外一种解法:
using System; using System.Collections.Generic; // http://acm.timus.ru/problem.aspx?space=1&num=1837 static class Timus { static readonly string VIP = "Isenbaev"; static SortedDictionary<string, int> ranks; static List<string[]> initial, prepare, ready; static void Main() { Initialize(); Compute(); foreach (var kvp in ranks) Console.WriteLine(kvp.Key + " " + ((kvp.Value < 0) ? "undefined" : kvp.Value.ToString())); } static void Initialize() { ranks = new SortedDictionary<string, int>(); initial = new List<string[]>(); ready = new List<string[]>(); for (var i = int.Parse(Console.ReadLine()); i > 0; i--) { var team = Console.ReadLine().Split(); var isReady = false; foreach (var name in team) { ranks[name] = (name == VIP) ? 0 : -1; if (name == VIP) isReady = true; } (isReady ? ready : initial).Add(team); } } static void Compute() { for (var rank = 1; ready.Count != 0; rank++, ready = prepare) { prepare = new List<string[]>(); foreach (var team in ready) foreach (var name in team) Compute(name, rank); } } static void Compute(string name, int rank) { if (ranks[name] >= 0) return; ranks[name] = rank; for (var i = 0; i < initial.Count; i++) foreach (var name2 in initial[i]) { if (name2 != name) continue; prepare.Add(initial[i]); initial.RemoveAt(i--); break; } } }
在这个程序中,取消了二维锯齿数组 teams 和一维数组 states,使用三个 List 代替,即: initial、prepare 和 ready,分别对应原来的 Initial、Prepare 和 Ready 状态。而 Done 状态不再需要了,ready 中的队伍处理完毕后,直接将 prepare 改为 ready 进入下一层次的循环。这个程序在大数据量的情况下可能会比第一个程序快一点。
扩展阅读
维基百科中的“埃尔德什数(Erdős number)”应该是这道题目的背景。与此相关的还有“六度分隔理论(Small world experiment)”。