论简单单淘汰赛制在icpc中的应用与解决方案

时间:2020-01-22 20:05:26   收藏:0   阅读:62

简单单淘汰赛制,即一些队伍两两比赛,一次直接淘汰败者,最终剩下一个队伍的赛制

在acm中我们有时候会遇见要解决此类问题的题目,对此有些心得想写下来

简单分析:

首先分析这种赛制需要计算的答案

可能是最终的胜者是谁,每个人获胜的概率是多少

再来分析这个问题的本质,实际上因为每个人只要输一次就会不再参加比赛,即每次数量减少的总是当前参赛者的1/2,那么就可以很好的用一棵二叉树表示,暂且不考虑轮空的情况,也就是参赛总人数为2的次方,那么这棵树就会是一棵满二叉树,对数据结构有所了解甚至说只用知道递归就可以很轻松的从条件中寻找出结果,直接build一棵满二叉树及其容易,叶子节点有n个(叶子节点代表的就是初始状态),满二叉树的大小(总节点数)就为n*2-1,那么复杂度就是总节点数(每个节点只用遍历一次就可以知道它的状态)

void build(int pos, int l, int r) {
    if (l == r) {
        p[pos] = a[l];//p是树的节点,l是原序列
        return;
    }
    int mid = l + r >> 1;
    build(pos << 1, l, mid);
    build(pos << 1 | 1, mid + 1, r);
    pushup(pos);//此操作因题而异
}

此时如果要求最终胜者,只需要比较值并且记录当前状态胜者是谁,最终根节点记录的人就是答案

求获胜概率的话

首先给出一种出题可能:

给出2^n 个人和一张图 mp[2^n][2^n],mp[i,j] 表示第i个人胜第j个人的概率

那么pushup的时候记录子节点每个人的获胜概率,直到根节点,根节点就有所有人的信息,即获胜概率

对于可能出现轮空的状况,即总人数不是2的次方,可以先补上k个人,k个人的权值(获胜概率)直接取0,然后使得总人数变为2的n次方即可得到答案

变形:

for _ in range(int(input())):
    n = int(input())
    a, b = [], []
    for i in range(1 << n):
        a.append((sorted(map(int, input().split())), i))
    while len(a) > 1:
        for i in range(0, len(a), 2):
            x0, i0 = a[i]
            x1, i1 = a[i + 1]
            index = [i0, i1][x0[-1] < x1[-1]]
            x0, x1 = sorted([x0, x1], key=lambda a: a[-1])
            for j in range(len(x1)):
                if x1[j] > x0[-1]:
                    break
            b.append((x1[:j] + x1[j + 1:], index))
        a, b = b, []
    print('Case #%d: %d' %(_ + 1, a[0][1] + 1))

? 首先分析单场比赛,两个人中最大值(现在仍然可用的)较小的那个一定会输,所以他按最优解一定会使用最大值,而另一个人就必须使用一个刚好大于这个值的值,然后晋级。

? 所以满二叉树中每个节点都储存当前胜者和当前胜者还可用的值的信息就可以了,总空间可以算一下,节点储存信息中当前胜者编号需要n*2-1个int,当前胜者可用值等于当前层数-1,即0*2+1*2+2*4+3*8+....+(n-1)*(2^n)

? 恩,反正我是算不清楚的,大概n<=14是不会爆的吧。

? 我的代码如下:

#include <bits/stdc++.h>
#define lson (pos << 1)
#define rson (pos << 1 | 1)
using namespace std;

const int maxn = 20000;
int n;
struct node {
    vector<int> val;
    int now;
}p[maxn << 2];

void pushup(int pos) {
    int ml = p[lson].val[p[lson].val.size() - 1];
    int mr = p[rson].val[p[rson].val.size() - 1];
    if (ml > mr) {
        p[pos].now = p[lson].now;
        int flag = 1;
        for (int i = 0; i < p[lson].val.size(); ++i) {
            if (flag && p[lson].val[i] > mr) {
                flag = 0;
                continue;
            }
            p[pos].val.push_back(p[lson].val[i]);
        }
    } else  {
        p[pos].now = p[rson].now;
        int flag = 1;
        for (int i = 0; i < p[rson].val.size(); ++i) {
            if (flag && p[rson].val[i] > ml) {
                flag = 0;
                continue;
            }
            p[pos].val.push_back(p[rson].val[i]);
        }
    }
}

void build(int pos, int l, int r) {
    p[pos].val.clear();
    if (l == r) {
        for (int i = 0, x; i < n; ++i) {
            scanf("%d", &x);
            p[pos].val.push_back(x);
        }
        sort(p[pos].val.begin(), p[pos].val.end());
        p[pos].now = l;
        return;
    }
    int mid = l + r >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
    pushup(pos);
}

int main(int argc,char *argv[])
{
    int t;
    scanf("%d", &t);
    for (int kase = 1; kase <= t; ++kase) {
        scanf("%d", &n);
        build(1, 1, (1<<n));
        printf("Case #%d: %d\n", kase, p[1].now);
    }
    return 0;
}

? 就是如刚才所说的,build然后pushup两种操作即可完成整个比赛。

原文:https://www.cnblogs.com/badcw/p/12229446.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!