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

CS50 Pset3 Tideman lock函数自测正常但Check50检测不通过

CS50 Tideman问题代码排查与修正

以下是针对CS50 2023年pset3 Tideman问题编写的代码:

#include <cs50.h>
#include <stdio.h>
#include <string.h>

// Max number of candidates
#define MAX 9

// preferences[i][j] is number of voters who prefer i over j
int preferences[MAX][MAX];

// locked[i][j] means i is locked in over j
bool locked[MAX][MAX];

// Each pair has a winner, loser
typedef struct
{
    int winner;
    int loser;
}
pair;

// Array of candidates
string candidates[MAX];
pair pairs[MAX * (MAX - 1) / 2];
int pair_count;
int candidate_count;

// Function prototypes
bool vote(int rank, string name, int ranks[]);
void record_preferences(int ranks[]);
void add_pairs(void);
void sort_pairs(void);
void lock_pairs(void);
void print_winner(void);
bool has_cycle(int start, int end); // 新增辅助函数声明

int main(int argc, string argv[])
{
    // Check for invalid usage
    if (argc < 2)
    {
        printf("Usage: tideman [candidate ...]\n");
        return 1;
    }

    // Populate array of candidates
    candidate_count = argc - 1;
    if (candidate_count > MAX)
    {
        printf("Maximum number of candidates is %i\n", MAX);
        return 2;
    }
    for (int i = 0; i < candidate_count; i++)
    {
        candidates[i] = argv[i + 1];
    }

    // Clear graph of locked in pairs
    for (int i = 0; i < candidate_count; i++)
    {
        for (int j = 0; j < candidate_count; j++)
        {
            locked[i][j] = false;
        }
    }

    pair_count = 0;
    int voter_count = get_int("Number of voters: ");

    // Query for votes
    for (int i = 0; i < voter_count; i++)
    {
        // ranks[i] is voter's ith preference
        int ranks[candidate_count];

        // Query for each rank
        for (int j = 0; j < candidate_count; j++)
        {
            string name = get_string("Rank %i: ", j + 1);

            if (!vote(j, name, ranks))
            {
                printf("Invalid vote.\n");
                return 3;
            }
        }

        record_preferences(ranks);

        printf("\n");
    }

    add_pairs();
    sort_pairs();
    lock_pairs();
    print_winner();
    return 0;
}

// Update ranks given a new vote
bool vote(int rank, string name, int ranks[])
{
    for (int i = 0; i < candidate_count; i++)
    {
        if (strcmp(name, candidates[i]) == 0)
        {
            ranks[rank] = i;
            return true;
        }
    }
    return false;
}

// Update preferences given one voter's ranks
void record_preferences(int ranks[])
{
    // 简化逻辑:遍历每个选民的排名,每个排名靠前的候选人都比后面的所有候选人更受偏好
    for (int i = 0; i < candidate_count; i++)
    {
        int preferred = ranks[i];
        for (int j = i + 1; j < candidate_count; j++)
        {
            int less_preferred = ranks[j];
            preferences[preferred][less_preferred]++;
        }
    }
    return;
}

// Record pairs of candidates where one is preferred over the other
void add_pairs(void)
{
    int idx = 0;
    for (int i = 0; i < candidate_count; i++)
    {
        for (int j = i + 1; j < candidate_count; j++)
        {
            if (preferences[i][j] > preferences[j][i])
            {
                pairs[idx].winner = i;
                pairs[idx].loser = j;
                idx++;
                pair_count++;
            }
            else if (preferences[i][j] < preferences[j][i])
            {
                pairs[idx].winner = j;
                pairs[idx].loser = i;
                idx++;
                pair_count++;
            }
            // 平局则不添加pair
        }
    }
    return;
}

// Sort pairs in decreasing order by strength of victory
void sort_pairs(void)
{
    for (int i = 0; i < pair_count; i++)
    {
        for (int j = 0; j < pair_count - i - 1; j++)
        {
            int strength_j = preferences[pairs[j].winner][pairs[j].loser];
            int strength_j1 = preferences[pairs[j+1].winner][pairs[j+1].loser];
            if (strength_j < strength_j1)
            {
                pair temp = pairs[j];
                pairs[j] = pairs[j+1];
                pairs[j+1] = temp;
            }
        }
    }
    return;
}

// 辅助函数:检测从start到end是否存在路径(用于判断循环)
bool has_cycle(int start, int end)
{
    if (start == end)
    {
        return true;
    }
    for (int i = 0; i < candidate_count; i++)
    {
        if (locked[start][i] && has_cycle(i, end))
        {
            return true;
        }
    }
    return false;
}

// Lock pairs into the candidate graph in order, without creating cycles
void lock_pairs(void)
{
    for (int i = 0; i < pair_count; i++)
    {
        int winner = pairs[i].winner;
        int loser = pairs[i].loser;
        // 检查添加winner→loser的边后,是否会形成循环(即loser能否走到winner)
        if (!has_cycle(loser, winner))
        {
            locked[winner][loser] = true;
        }
    }
    return;
}

// Print the winner of the election
void print_winner(void)
{
    // 寻找入度为0的节点:没有任何候选人锁定击败他
    for (int i = 0; i < candidate_count; i++)
    {
        bool is_source = true;
        for (int j = 0; j < candidate_count; j++)
        {
            if (locked[j][i])
            {
                is_source = false;
                break;
            }
        }
        if (is_source)
        {
            printf("%s\n", candidates[i]);
            return;
        }
    }
    return;
}

问题根源分析

你的代码在lock_pairs函数中的循环检测逻辑完全错误:

  • 当前逻辑是先全部锁定所有pair,再统计每个候选人作为loser的次数,若次数等于pair_count就解锁最后一个pair。这种方式只能处理极端的全循环场景,无法检测中间pair添加时形成的循环。
  • 正确的循环检测逻辑应该是:在添加每个pair(按强度从高到低)之前,检查添加这条winner→loser的边后,是否存在从loser到winner的路径。如果存在路径,说明添加这条边会形成循环,不能锁定;否则可以安全锁定。

另外,print_winner函数也存在逻辑错误:

  • 你遍历的是pair_count而非candidate_count,可能遗漏候选人;正确的做法是寻找入度为0的节点(即没有任何其他候选人锁定击败他的节点),这才是Tideman选举的胜者。

修正说明

  1. 新增has_cycle辅助函数:通过递归DFS检测从start到end是否存在路径,用于判断添加边是否会形成循环。
  2. 重写lock_pairs函数:按排序后的顺序遍历每个pair,仅当添加该边不会形成循环时才锁定。
  3. 简化record_preferences函数:原逻辑过于复杂且易出错,简化为直接遍历选民的排名,靠前的候选人比所有后续候选人的偏好计数加1。
  4. 修正add_pairs函数:原逻辑会重复处理i和j的组合(比如i=0,j=1和i=1,j=0),改为仅处理i<j的组合,避免重复添加pair。
  5. 修正print_winner函数:遍历所有候选人,找到入度为0的节点并输出。

这些修正后,代码可以正确处理所有循环场景,通过Check50的测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 09:29:53