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