Codeforces Rockethon 2014 C题

今晚实在是看不下去题目了,就将codeforces里面官方给出的答案给翻译了一遍~

391C3 - The Tournament

这是一道有多个要求的题目,我在这里解释第三组样例。输入如下:

5 2

2 10

2 10

1 1

3 1

3 1

常见的误解是赢得最后三个对手就可以达到前2.注意仅仅赢得了最后三个意味着Manao输掉了前面两个,所以他们就会有更Manao一样的分数而且名次会在他之前。最佳的胜利的策略是赢得前两个中的一个,并且赢得另外两场需要花费1个单位effort的fighter。

Subproblem C1

按照条件约束所示,这里预期的方法是暴力。注意这里仅仅有2的n次方不同可能的情况,决定与Manao赢哪场输哪场。这样,我们可以得到所有这些情况,计算出Mamao将会得到的分数与其他竞争者新的分数,决定一个Manao可以到达前k名的情况中总共需要最少的effort。

我们怎么可以得到所有的情况?一个很自然的方法就是递归:对于每一次比赛,我们选择结果(赢或者输)并且递归更深的一层,当每一场比赛的结果被选择并且估计完成后停止。然而,另一种方法通常会用更少的时间:位运算(原文貌似是位掩码)。如果你对位运算不熟悉,我建议读读这篇很久但是并不过时的bmerry写的tutorial dimkadimon's Topcoder Cookbook recipe on iterating over subsets。下面是用很短的位运算解决这个问题的伪代码:

ans = INFINITY
    for(bitmask = 0; bitmask < 2**n; bitmask++)
        int score = 0;
        int effort = 0;
        for(i = 0; i < n; i++)
            if (bitTurnedOnIn(mask, i))
                score = score + 1;
                effort = effort + fighter[i].effort;
        rank = n + 1;
        for(int i = 0; i < n; i++)
            if (fighter[i].score < score - 1 || fighter[i].score <= score && (bitTurnedOnIn(mask, i))
                rank = rank - 1
        if (rank <= k)
            ans = min(ans, effort);

Subproblem C2.

这个问题可以用任何合理的多项式级的算法解决。我的方法如下:

遍历所有Manao最终会得到的分数P,检查P分是否可能最终得到前k名还有所需的最小的effort。这个可以用动态规划计算:让F(i,j,k)表示在前i个选手中赢得k场并且排名在其中j人后面所需的最少的effort。。我不会详述这个方法,因为这里有很多可替代的方法,而且这个也不是最令人满意的方法。

Subproblem C3.

让我们更深层的来看Manao要达到前k名需要多少分。因为得到前k名和他排名在最后n-k+1人前是一个意思,我就从现在开始只假设他需要打败Q=n-k+1个竞争者。S(i)表示在Manao加入锦标赛之前有i分的人数。然后,如果S(0)+S(1)+S(2)+...+S(P)比Q要小,那么Manao不可能仅仅最后有P分就比Q人名次好。所以我们有一个Mamao最终得分的更低的界限。另一方面,注意如果S(0)+S(1)+...+S(P-2)大于等于Q,那么在得到P分以后Manao将会确切的在Q个竞争对手名次前面。这样,如果P‘是使得 S(0) + S(1) + … + S(P' - 2) ≥ Q的最少的分数,就没有必要得到更多的分数。Manao可以仅仅赢P’场对他来说需要effort最少的比赛。这些观察给我们仅仅两个有趣的P值——第一个值是当S(0) + S(1) + … + S(P) ≥ Q另一个是这个值加一。

让这些情况变得复杂的是哪些比赛Manao赢了或输了会产生影响。假设Manao想得到P分。设S' = S(0) + S(1) + … + S(P - 2)。这个是假设Manao得到P分以后必然比他名次低的数量。每一个有P-1或者P分的竞争者在Manao输掉相应比赛是会比他名次好,反之就会差。这样,Manao需要赢得至少Q - S' 场跟这些竞争者的比赛。我们将打败每一个分数为P-1或者P的竞争者的effort放入一张表中,把打败剩下的需要的effort放入另外一张表中。我们排序两张表以后,我们所需要做的是找出最少的Q个数字的和使得至少他们中至少Q - S'个来自第一个表中剩余的来自第二张表。

这个算法的复杂度是O(NlogN).

Bonus:你能在线性时间内解决这个问题吗?


391C3 - The Tournament

Since it was a question of multiple clarification requests, I will explain the third example here. The input is as follows:

5 2
2 10
2 10
1 1
3 1
3 1

The common misconception was that winning against three last opponents is sufficient to get in top 2. Note that winning only against the last three implies that Manao lost against the first two, so they'll have as many points as Manao and rank better than him. The optimal strategy is to win against one of the first two fighters, and win another two matches against fighters costing 1 unit of effort.

Subproblem C1.

As the constraints suggest, the intended solution here is brute force. Note that there are only 2n different possible scenarios, depending on which fight Manao wins and which he loses. Thus, we can generate all of these scenarios, compute the score that Manao will obtain as a result of it and what will the other contestants' new scores be, and determine what is the minimum total effort Manao needs for a scenario which gets him in top k.

How can we generate all scenarios? A natural way to do this is recursion: for each fight, we choose the outcome (win or loss) and recurse one level deeper, stopping when all match outcomes have been chosen and performing the evaluation. However, there is another way which is usually less time-consuming: bitmasks. If you are not familiar with using bitmasks, I recommend reading the old-but-never-outdated tutorial by bmerry and dimkadimon's Topcoder Cookbook recipe on iterating over subsets. This is a pseudocode showing how short a bitmask solution for this problem is:

    ans = INFINITY
    for(bitmask = 0; bitmask < 2**n; bitmask++)
        int score = 0;
        int effort = 0;
        for(i = 0; i < n; i++)
            if (bitTurnedOnIn(mask, i))
                score = score + 1;
                effort = effort + fighter[i].effort;
        rank = n + 1;
        for(int i = 0; i < n; i++)
            if (fighter[i].score < score - 1 || fighter[i].score <= score && (bitTurnedOnIn(mask, i))
                rank = rank - 1
        if (rank <= k)
            ans = min(ans, effort);

Subproblem C2.

This subtask could be solved with any reasonable polynomial algorithm. My solution was the following: iterate over the number of pointsP that Manao will have in the end. For each P, let us check whether it is possible to finish in top k having P points and what is the minimum effort required. This can be computed using dynamic programming: let F(i, j, k) be the minimum effort required to win k of the fights against the first i fighters and be ranked worse than j of them. I will not go into details of this solution, since there are many alternatives, and it is suboptimal anyway.

Subproblem C3.

Let us look deeper into what amount of points Manao might have if he finishes in top k. Since finishing in top k is the same as ranking better than at least n - k + 1 opponent, I will just assume from now on that he has to beat Q = n - k + 1 opponents. Let S(i) be the number of fighters that have i points before Manao joins the tournament. Then, if S(0) + S(1) + … + S(P) is less than Q, there is no way Manao can possibly rank better than Q opponents if he has score P in the end. So we have a lower bound on the score Manao will have in the end. On the other hand, note that if S(0) + S(1) + … + S(P - 2) is more than or equal to Q, then after scoring P points Manao will surely rank better than at least Q rivals. Thus, if P' is the minimum amount of points such that S(0) + S(1) + … + S(P' - 2) ≥ Q, there is no need to go for more points. Manao can just win the P' matches which require the least amount of effort from him. These observations leave us with only two interesting values of P — the first value when S(0) + S(1) + … + S(P) ≥ Q and that value plus 1.

What makes these cases more complicated is the fact that now it matters which matches does Manao win or lose. Suppose Manao wants to score P points. Let S' = S(0) + S(1) + … + S(P - 2). This is the count of opponents that are definitely ranked lower than Manao if he gets P points. Each of the fighters that has P - 1 or P points will rank better than Manao if Manao loses the corresponding fight and worse otherwise. Thus, Manao needs to win at least Q - S' of the fights against those opponents. Let's collect the efforts required to beat each of the opponents with score P - 1 or P into a list and collect the efforts required to beat each of the other opponents in another list. After we sort both lists, all we need to do is find the minimum sum of Q numbers such that at least Q - S' of them are from the first list and the rest are from the second.

The complexity of this algorithm is O(NlogN).

Bonus: Can you solve the problem in linear time?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值