You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CS50 Tideman:lock_pairs函数循环检测逻辑问题咨询

CS50 Tideman项目lock_pairs函数问题解析

我在CS50的Tideman项目中,为lock_pairs函数调试了好几天,自认为逻辑没问题,但check50唯独在“lock_pairs skips final pair if it creates cycle”测试项上失败。找到一份递归实现的lock_pairs代码后,所有测试都通过了,却搞不懂两者的差异。我试过用debug50和绘图分析,还是没找到关键区别,希望有人分步解释这份递归函数为什么正确,以及我的代码哪里出了问题。


正确的递归实现代码

// 递归函数:检查添加当前配对是否会形成环
bool makes_circle(int cycle_start, int loser)
{
    if (loser == cycle_start)
    {
        // 当前遍历到的节点等于环的起点,说明形成了环
        return true;
    }
    for (int i = 0; i < candidate_count; i++)
    {
        if (locked[loser][i])
        {
            if (makes_circle(cycle_start, i))
            {
                // 沿着锁定的边继续遍历,找到环则返回true
                return true;
            }
        }
    }
    return false;
}

// 按顺序锁定配对,确保不形成环
void lock_pairs()
{
    for (int i = 0; i < pair_count; i++)
    {
        if (!makes_circle(pairs[i].winner, pairs[i].loser))
        {
            // 只有当添加该配对不会形成环时,才锁定它
            locked[pairs[i].winner][pairs[i].loser] = true;
        }
    }
}

递归代码的正确性分析

  • 核心逻辑:深度遍历检测环
    当要锁定(winner, loser)配对时,核心问题是:从loser出发,沿着已锁定的胜负边,能不能走到winner?如果能,就会形成winner → loser → ... → winner的环,不能锁定;反之则可以锁定。
    递归函数makes_circle就是做这件事:以当前配对的winner为目标起点,从loser开始深度优先遍历所有已锁定的边,只要找到一条路径回到起点,就判定会形成环。
  • 覆盖所有环结构
    递归会沿着每一条边不断深入,不管路径长度是2、3还是更长。比如存在loser → A → B → ... → winner的长路径时,递归会一步步遍历到A、B直到找到起点,不会漏掉任何可能的环。

我的代码实现

void lock_pairs(void)
{
    for (int i = 0; i < pair_count; i++)
    {
        bool cycle = false;
        // 检查当前配对的loser是否击败过其他候选人
        for (int j = 0; j < candidate_count && !cycle; j++)
        {
            if (locked[pairs[i].loser][j])
            {
                // 如果loser击败了j,检查j是否击败了当前配对的winner
                for (int k = 0; k < candidate_count && !cycle; k++)
                {
                    if (locked[j][k])
                    {
                        if (k == pairs[i].winner)
                        {
                            cycle = true;
                        }
                    }
                }
            }
        }
        if (!cycle)
        {
            locked[pairs[i].winner][pairs[i].loser] = true;
        }
    }
    return;
}

我的代码的问题所在

你的代码只检测了长度为3的环(winner → loser → j → winner),完全忽略了更长的环结构:

  • 比如存在winner → loser → A → B → winner这样的环时,你的代码只会检查loser直接指向的j,然后看j是否直接指向winner,不会继续遍历A、B的后续路径。
  • 当测试项要求跳过会形成环的最后一个配对时,大概率是出现了长度大于3的环,你的代码没检测出来,错误地锁定了配对,导致环形成,所以测试失败。

举个具体例子:
假设候选人是W(当前配对的winner)、L(loser)、A、B,已锁定的边是L→A、A→B、B→W。现在要添加W→L,你的代码只会检查L指向的A,发现A不直接指向W,就判定没有环,错误锁定了W→L;但递归代码会从L出发,遍历到A、B,最后到W,发现等于起点,判定有环,不会锁定这条边。


内容的提问来源于stack exchange,提问作者Adam Oparka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 03:25:17