1 solutions

  • 0
    @ 2025-5-20 22:07:19

    我们对选手的分组情况进行全排列枚举,每一种排列进行模拟,判断 1 号玩家是否为赢家即可。由于全排列当中可能会出现初始分组重复的情况(两个同一组的选手正反顺序算不同排列),枚举之后的答案除以 2n/22^{n/2} 即可。时间复杂度 O(n!)O(n!)

    作为签到题,本题相比往年题目而言,从考察基础的 for 循环改为考察指数型/排列型枚举。对于基础较为薄弱的同学而言会较为棘手。

    需要注意的是,本题的样例几乎没有任何用处,需要考生对这部分知识点熟练掌握。

    全排列的基础写法有两种,基础的深度优先搜索方式可以看这篇题解,这里我们提供一种更简洁的基于 STL 函数 next_permutation 的写法。该函数每次会用 O(n)O(n) 的复杂度给出当前排列按照字典序的下一个排列是什么,因此从字典序最小的排列开始枚举,直至无法循环为止即可。

    #include <stdio.h>
    #include <algorithm>
    int n;
    int pos[10];
    int a[10][10];
    int ans;
    
    int win[10];
    bool judge()
    {
        // 初始化:把排列做好
        for (int i = 1; i <= n; ++i) win[i] = pos[i];
        int tmp_n = n >> 1;
        while (tmp_n) // 淘汰轮次还在继续
        {
            for (int i = 1; i <= tmp_n; ++i) // 第 i 个赢家从第 i*2-1 个人和第 i*2 个人决出来,不会出现数组被覆盖导致信息错误。
                win[i] = a[win[(i << 1) - 1]][win[i << 1]] > 0 ? win[(i << 1) - 1] : win[i << 1];
            tmp_n >>= 1; // 每轮淘汰赛完成后,人员减半
        }
        return win[1] == 1; // 赢家是 1 那就是我们要的结果
    }
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                scanf("%d", &a[i][j]);
        for (int i = 1; i <= n; ++i)
            pos[i] = i;
        do
        {
            ans += judge();
        } while (std::next_permutation(pos + 1, pos + n + 1)); // 枚举所有排列
        printf("%d", ans >> (n >> 1));  
        // 除以重复计算的 2^(n/2) 即可,使用位运算即可表示乘除 2 的次幂。
    }
    
    • 1

    Information

    ID
    24
    Time
    1000ms
    Memory
    512MiB
    Difficulty
    1
    Tags
    # Submissions
    8
    Accepted
    3
    Uploaded By