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)”。