Facebook黑客杯:权力压倒性

时间:2011-01-15 17:52:21

标签: algorithm math

Facebook的很多人都喜欢玩星际争霸II™。其中一些人使用星际争霸II™地图编辑器制作了一个自定义游戏。在这个游戏中,你扮演高贵的神族,从一个巨大的虫族军队中捍卫你收养的Shakuras家园。在被淹没之前,你必须尽可能多地对虫族造成伤害。你只能建造两种类型的单位,盾牌发电机和战士。盾牌发生器不会造成伤害,但是你的军队每生成一个盾牌发生器就可以存活一秒钟。勇士每秒造成一次伤害。盾牌发生器到期后,你的军队立即超支。你应该建造多少盾牌发生器和多少战士,以便在你的军队超支之前对虫族施加最大的伤害?因为Protoss重视勇敢,如果有多个解决方案,你应该返回使用最多勇士的那个。

约束

  • 1≤G(一个屏蔽发生器的成本)≤100
  • 1≤W(一个战士的成本)≤100
  • G +W≤M(可用资金)≤1000000000000(10 12

6 个答案:

答案 0 :(得分:7)

这是一个复杂度为 O(W)的解决方案。设 g 是我们建造的发电机数量,同样让 w 为我们建造的战士数量(并且 G,W 为相应的每单位价格)。

我们注意到我们希望最大化 w * g ,但 w * W + g * G <= M

首先,我们将摆脱其中一个变量。请注意,如果我们选择 g 的值,那么显然我们应该尽可能多地购买剩余金额 M - g * G 的战士。换句话说, w = floor((M-g * G)/ W)

现在,问题是最大化 g * floor((Mg * G)/ W) 0 <= g <=楼层(M / G)。我们想摆脱这个问题,所以让我们考虑 W 不同的情况。让我们写 g = W * k + r ,其中 0&lt; = r&lt; W 是将 g 除以 W 时的余数。

现在的想法是修复 r ,并插入 g 的表达式,然后让 k 成为等式中的变量。我们将在 k 中得到以下二次方程:

p = floor((M - r * G)/ W),则方程为( - GW)* k ^ 2 +(Wp - rG)k + rp

这是一个二次方程,当x变为无穷大或负无穷大时,它变为负无穷大,因此它在 k = -B /(2A)时具有全局最大值。为了找到k的合法值的最大值,我们将尝试k的最小合法值,k的最大合法值以及实际最大值的两个最接近的整数点(如果它们在合法范围内)。

r 的所有值的总体最大值是我们正在寻找的值。由于 r W 值,并且 O(1)计算固定值的最大值,因此总时间< b> O(W)

答案 1 :(得分:5)

如果您构建 g 生成器,以及 w 战士,则可以造成全部伤害 w (每次伤害)× g (游戏结束前的时间)。

资金限制将 g w 的值限制为 W < / em> × w + G × g M

如果您构建 g 生成器,则最多可以构建( M - g × G )/ W 战士,并做 g ×( M - g × < em> G )/ W 损坏。

此功能的最大值为 g = M /(2 G ),这会导致 M 2 /(4 G W )损坏。

要点:

  • 构建 M /(2 G )盾牌生成器。
  • 构建 M /(2 G )战士。
  • M 2 /(4 G W )损坏。

由于您只能构建两个单位中的任何一个的整数,这会减少优化问题:

  

最大化 g × w
  相对于 g × G + w × W M g w ∈ℤ +

Integer Programming的一般问题是NP完全的,所以最好的算法是检查接近上面的实值解的所有整数值。

如果您找到一对( g i w i ),总损失 d i ,您只需要检查 g <的值sub> j × w j d i 。这和原始条件 W × w + G × g M 会限制搜索空间中找到的每个项目。


F#-code:

let findBestSetup (G : int) (W : int) (M : int) =
    let mutable bestG = int (float M / (2.0 * float G))
    let mutable bestW = int (float M / (2.0 * float W))
    let mutable bestScore = bestG * bestW
    let maxW = (M + isqrt (M*M - 4 * bestScore * G * W)) / (2*G)
    let minW = (M - isqrt (M*M - 4 * bestScore * G * W)) / (2*G)
    for w = minW to maxW do
        // ceiling of (bestScore / w)
        let minG = (bestScore + w - 1) / w
        let maxG = (M - W*w)/G
        for g = minG to maxG do
            let score = g * w
            if score > bestScore || score = bestScore && w > bestW then
                bestG <- g
                bestW <- w
                bestScore <- score
    bestG, bestW, bestScore

答案 2 :(得分:1)

假设W和G是计数,每个的成本等于1.所以它已经过时了更新的问题。

伤害=生命时间* DamagePerSecond = W * G

所以你需要用约束G + W <= M来最大化W * G.由于发生器和勇士都是好的,我们可以使用G + W = M.

因此,我们想要最大化的函数变为W *(M-W) 现在我们设置衍生物= 0:
M-2W = 0
W = M / 2

但是因为我们需要离散情况的解决方案(你不能拥有x.5战士和x.5发生器)我们使用最接近连续解的值(由于parabel的属性,这是最优的)

如果M是偶数,则连续解与分立解相同。如果M是奇数,那么我们有两个最接近的解,一个比一个战士多于发生器,另一个反过来。 OP说我们应该选择更多的战士。

所以最终的解决方案是:
对于偶数M,G = W = M / 2 对于奇数M,G + 1 = W =(M + 1)/ 2。

答案 3 :(得分:0)

g =总发电机数 gc =发电机成本 w =战士 wc =战士成本 米=钱 d =总伤害

g =(m - (w * wc))/ gc w =(m - (g * gc))/ wc

d = g * w d =((m - (w * wc))/ gc)*((m - (g * gc))/ wc) d =((m - (w * wc))/ gc)*((m - (((m - (w * wc))/ gc)* gc))/ wc)作为战士函数的伤害

然后我尝试计算所有损坏的数组然后找到最大值,但当然它不会在6分钟内完成数万亿的m。

要找到最大值,你必须区分那个等式并找出它何时等于零,我忘记了怎么做seing我在大约6年没做过数学

答案 4 :(得分:0)

这不是一个真正的解决方案,但现在可以了。

假设当盾牌数量等于1(不能等于零或不会造成伤害)并且战士数量等于(m-g)/w时,你已经获得了很高的伤害值。迭代应该(再次作出假设)达到在最大化损害的盾牌和战士数量之间达成妥协的程度。这由bestDamage > calc分支处理。

这种推理几乎可能存在缺陷,理解问题背后的数学更为可取。由于我没有练习数学一段时间,我只是猜测这需要推导出一个函数。

        long bestDamage = 0;
        long numShields = 0;
        long numWarriors = 0;

        for( int k = 1;; k++ ){
            // Should move declaration outside of loop
            long calc = m / ( k * g ); // k = number of shields

            if( bestDamage < calc ) {
                bestDamage = calc;
            }

            if( bestDamage > calc ) {
                numShields = k;
                numWarriors = (m - (numShields*g))/w;
                break;
            }
        }

        System.out.println( "numShields:" + numShields );
        System.out.println( "numWarriors:" + numWarriors );
        System.out.println( bestDamage );

答案 5 :(得分:0)

自昨晚我解决了这个问题后,我想我会发布我的C ++解决方案。该算法以初始猜测开始,位于连续情况的全局最大值。然后它在初始猜测的左/右搜索“小”,当连续情况下降到已经建立的最大值以下时,提前终止。有趣的是,FB发布的5个示例答案包含3个错误答案:

Case #1
  ours:   21964379805 dmg: 723650970382348706550
  theirs: 21964393379 dmg: 723650970382072360271 Wrong
Case #2
  ours:   1652611083 dmg: 6790901372732348715
  theirs: 1652611083 dmg: 6790901372732348715 
Case #3
  ours:   12472139015 dmg: 60666158566094902765
  theirs: 12472102915 dmg: 60666158565585381950 Wrong
Case #4
  ours:   6386438607 dmg: 10998633262062635721
  theirs: 6386403897 dmg: 10998633261737360511 Wrong
Case #5
  ours:   1991050385 dmg: 15857126540443542515
  theirs: 1991050385 dmg: 15857126540443542515

最后代码(它使用libgmpxx为大数字)。我怀疑代码是最优的,但它在我的个人计算机上以0.280ms完成,以获得FB给出的示例输入....

#include <iostream>
#include <gmpxx.h>

using namespace std;

typedef mpz_class Integer;
typedef mpf_class Real;

static Integer getDamage( Integer g, Integer G, Integer W, Integer M)
{
    Integer w = (M - g * G) / W;
    return g * w;
}

static Integer optimize( Integer G, Integer W, Integer M)
{
    Integer initialNg = M / ( 2 * G);
    Integer bestNg = initialNg;
    Integer bestDamage = getDamage ( initialNg, G, W, M);

    // search left
    for( Integer gg = initialNg - 1 ; ; gg -- ) {
        Real bestTheoreticalDamage = gg * (M - gg * G) / (Real(W));
        if( bestTheoreticalDamage < bestDamage) break;
        Integer dd = getDamage ( gg, G, W, M);
        if( dd >= bestDamage) {
            bestDamage = dd;
            bestNg = gg;
        }
    }

    // search right
    for( Integer gg = initialNg + 1 ; ; gg ++ ) {
        Real bestTheoreticalDamage = gg * (M - gg * G) / (Real(W));
        if( bestTheoreticalDamage < bestDamage) break;
        Integer dd = getDamage ( gg, G, W, M);
        if( dd > bestDamage) {
            bestDamage = dd;
            bestNg = gg;
        }
    }

    return bestNg;
}

int main( int, char **)
{
    Integer N;

    cin >> N;
    for( int i = 0 ; i < N ; i ++ ) {
        cout << "Case #" << i << "\n";
        Integer G, W, M, FB;
        cin >> G >> W >> M >> FB;

        Integer g = optimize( G, W, M);

        Integer ourDamage = getDamage( g, G, W, M);
        Integer fbDamage = getDamage( FB, G, W, M);

        cout << "  ours:   " << g << " dmg: " << ourDamage << "\n"
             << "  theirs: " << FB << " dmg: " << fbDamage << " "
             << (ourDamage > fbDamage ? "Wrong" : "") << "\n";
    }
}