【问题标题】:Linear algorithm for finding the celebrity group, not single celebrity寻找名人组的线性算法,而不是单个名人
【发布时间】:2014-01-07 22:26:35
【问题描述】:

假设我们有 N 个人,其中可能有一群名人。

每个人都认识每个名人,而每个名人都只认识其他名人。

如果给你know x yreturns true or false的功能,请识别名人组。

这个问题是识别一群名人,而不是识别人群中唯一的名人,比如http://www.geeksforgeeks.org/the-celebrity-problem/


使用蛮力很容易。我可以构建 N 个人所有可能的子序列并使用条件过滤它们(每个人都知道每个名人,每个名人只知道其他名人)。

我也知道必须只有一个名人团体或没有。

证明:

假设我们有两个名人组,C1 和 C2。因为每个人都知道 C1 中的 ci,所以每个 C2 中的 cj 也都知道 ci;对称地,每个 ci 都知道 cj;所以 C1 和 C2 实际上属于一个组。所以我们最多只有一个名人团体或没有。

对可能的线性算法有什么想法吗?

编辑

可能有一群名人,也可能没有。

【问题讨论】:

  • 一个名人认识其他名人?比如,任何一个名人都认识其他所有名人?
  • 一个非名人是否也能认识任何数量的其他非名人?
  • 每个人都只认识名人还是两个非名人可以互相认识?
  • 最具挑战性的情况是如果每个人都认识每个人,因为这需要 N*(N-1) 次调用 know 来验证每个人都是名人。
  • 答案难道不是每个人认识的人的交集吗?

标签: algorithm


【解决方案1】:

是的,这在 O(N) 中是可能的(但请参阅下面的第二次编辑)。这是一种算法。

枚举从 0 到 N-1 的所有 N 个人。

int find_a_celebrity()
{
    int C = 0; // C is a potential celebrity
    for( int i=0 ; i<N ; ++i )
      if( !know(i,C) ) // C is not a celebrity nor are all j<i, but i might be.
        C = i; 
    for( int i=0 ; i<N ; ++i ) // Loop a second time to check everyone knows C.
      if( !know(i,C) ) return -1;
    return C;
}
int C = find_a_celebrity();

如果C==-1 则没有名人。否则集合{ y | know(C,y) } 是所有名人的集合。总之,这在所有 N 个人中最多需要 3 次迭代,所以这是及时发现的 O(N)

编辑:

// Output the set of celebrities
if( C == -1 ) std::cout << "There are no celebrities.";
else for( int i=0 ; i<N ; ++i ) if( know(C,i) ) std::cout << i << ' ';
std::cout << std::endl;

编辑 2:

这个问题有两种解释:

  1. 名人的定义是每个人都知道他们。受问题约束,所有名人只认识其他名人。
  2. 名人的定义是人人皆知,名人只认识其他名人。

上述算法解决了案例 #1 的问题。这也适用于案例 #2,只要我们可以假设至少存在一个名人。否则,我们将不得不在最后验证潜在名人列表仅彼此认识,这需要 O(N*M) 时间,其中 M 是潜在名人的数量。

【讨论】:

  • @JacksonTale 完成。希望对您有所帮助。
  • 所有非名人不一定互相认识,所以我认为这行不通(你从 C = 非名人 A 开始,非名人 B 不知道) A,因此您将 C 分配给 B,然后返回 -1,因为每个人都不知道 B)。
  • @Dukeling 说的是真的。我们不能假设所有非名人都认识。
  • @Dukeling 是的,没错,所有非名人可能都不认识对方,这不是假设的。您的示例不完整 - 是的 C=B,但它仍会检查列表的其余部分是否有其他潜在名人。如果找到,则将其分配给 C。如果没有,则没有名人,-1 是正确的返回值。
  • 假设我们有 3 个人:0、1 和 2。1 和 2 知道 0,所以从你的第一个循环中,C = 0。第二个循环也会给出 C = 0。假设 0 知道 1但是 2 不知道 1,那么你的第三个循环也会给 1 作为名人,但这不是真的,因为 2 不知道 1。
【解决方案2】:

编辑:我没有仔细阅读问题陈述。您没有所有边的列表,我们假设我们无法在线性时间内生成它。

这里的关键是每个人都认识每个名人。将此问题表述为一个有向邻接图,其中 V 个顶点(名人)和 E 条边(关系)连接它们。

遍历列表 E 以计算每个人被认识的次数。名人已知V-1次;其他人都是非名人。运行时间应该是 O(E)。

【讨论】:

  • 我也想过一个图,但是构建图会很昂贵(已经有二次关系)。
  • 严格来说,你只需要名人列表和关系列表;您不需要实际的矩阵或二维数组来实现图形。
  • 是的,但你没有关系列表,你只有一个函数来确定两个人的关系。
  • 哦,对了……我以前见过这个问题,让我看看我是否能记住线性时间解决它背后的技巧。
  • @Daniel 不是这样。如果每个人都认识每个人,你可以从一个叫 C 的人开始。检查每个人都认识 C。那么 C 是名人。根据定义,C 认识的其他所有人也是名人,实际上就是所有名人,而你在 O(N) 中完成。看我的回答。
【解决方案3】:

成为名人是在不知不觉中被人熟知的状态。正常是在不被人知道的情况下知道的状态。因此,将每个人与他们旁边的人进行比较看起来像:

foreach person in persons
{
  knows = know(person, next_person)
  isknown = know(next_person, person)

  if knows and !isknown then normal
  if !knows and isknown then celeb
  if !knows and !isknown then normal
  if knows and isknown then friends.add(person)

  foreach friend in friends
  {
      alsoknows = know(person, friend)
      if !alsoknows then normal; break;
  }
}

【讨论】:

  • 构建好友子列表是一种有趣的方法。我认为您的标准有点偏离,因为您不能仅仅因为另一个人认识他们就确定那个人是名人;每个人都必须认识他们。但是创建子列表的想法仍然很有趣。但是,您必须在“朋友”列表上嵌套循环,以确保您的最终列表是详尽且排他的。在每个人都知道 next_person 的情况下,您的朋友列表将是起始“人”列表的克隆,这意味着它仍然是 O(N^2)
  • 是的,我的解决方案有缺陷。我只是把它留下来,因为我认为它可能会提供一些概念。我的妻子告诉我不要再试图解决这个问题了:)
  • @isick 我应该补充一点,你绝对可以说某人是名人,因为另一个人认识他们而他们不认识那个人。
【解决方案4】:

考虑所有 N*(N-1) 个可能对 know(x,y) 的评估都为真的情况。在这种情况下,所有 N 个人都是名人。接下来,考虑除了一个对 know(x,y) 的 N*(N-1) 次评估之外的所有情况都为真的情况。根据this comment by the asker,我希望我们应该将这种情况解释为名人为零。为了区分这两种情况,您需要评估所有可能的 N*(N-1) 对。

【讨论】:

    【解决方案5】:

    candidates 成为所有n 人员的列表。构建一个所有人的循环:

    1 -&gt; 2 -&gt; ... -&gt; n -&gt; 1.

    现在,一一检查knows k,k+1。如果比k = k+1 为真,然后继续。如果knows k,k+1 == False那么k+1 不是名人,所以将他从candidates 中删除并改为检查k,k+2

    当我们在最后一个 size(candidates) 回合中没有删除任何人时,我们就完成了。

    证明:

    如果列表中有名人,则:

    1. 他永远不会被删除
    2. 一旦我们找到他,我们就会进一步找到最后一位名人
    3. 算法将在“最后一个”之后删除所有非名人,直到我们找到另一个名人,然后我们回到 2。

    最后我们会得到一个名人循环。

    如果列表中没有名人,请关注@Matt 的回答。

    【讨论】:

    • 不正确。假设 0 不知道 1 或 2,并且 1 也不知道 0。在你的算法中,0 将是唯一剩下的,但这不是正确的答案
    • 如果 0 不知道 1,那么 1 不是名人,我们可能会删除他 - 他提供的信息在我的算法上下文中是多余的。现在,如果 0 留在循环中并且至少有 1 个名人,那么在某个点 0 将被删除。如果循环中没有名人,则与“里面有一群名人”相矛盾(我假设它的大小是正数)。
    • 附言。如果我们包括根本没有名人的可能性,那么就不可能解决问题。对于有名人的每一组人,我们可以构建另一个具有完全相同关系knows但没有名人的组。所以我们无法区分这两种情况。
    • +1 不错。就像我的回答一样,如果我们知道至少存在 1 个名人,这将有效。否则,这只会​​产生一个潜在名人列表,然后需要 O(N*M) 时间来验证,其中 M 是结果列表的大小。
    • 其实如果没有名人和一群装模作样(非名人只知道彼此)我们将无法验证。
    猜你喜欢
    • 2012-01-15
    • 1970-01-01
    • 2015-07-01
    • 2011-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多