/* PROBLEM DESCRIPTION: * We have a set budget, that we need to split between agents who can divide their time between a set of actions, to maximize the resulting gain. * * OUTPUT: * selections[] is the selected action for each agent * if interpSet is >= 0, then it denotes the only agent that splits its time between actions: (1 - interpT) time doing selections[interpSet] and interpT time doing interpTarget * * NOTE: will clobber costs and gains arrays */ public static void FindBestBudgetSplit(double[] costs, double[] gains, int l, int n, double budget, out int[] selections, out int interpSet, out double interpT, out int interpTarget) { int[] hulls = new int[n * l]; int[] hullends = new int[n]; double[] scratch = new double[l]; for (int i = 0; i < n; ++i) { hullends[i] = i * l + FindUpperIncreasingConvexHullPoints(costs, gains, hulls, i * l, l, scratch); } DoubleMaxHeap <int> pq = new DoubleMaxHeap <int>(); int[] cursors = new int[n]; for (int i = 0; i < n; ++i) { int c = i * l; cursors[i] = c; budget -= costs[c]; if ((c + 1) < hullends[i]) { pq.Push((gains[c + 1] - gains[c]) / (costs[c + 1] - costs[c]), i); } } interpSet = -1; interpT = 0; interpTarget = -1; while (pq.Count > 0 && budget > 0) { int i = pq.TopValue; pq.Pop(); int c = cursors[i]; double deltacost = costs[c + 1] - costs[c]; if (deltacost < budget) { budget -= deltacost; cursors[i] = ++c; if ((c + 1) < hullends[i]) { pq.Push((gains[c + 1] - gains[c]) / (costs[c + 1] - costs[c]), i); } } else { interpSet = i; interpT = budget / deltacost; interpTarget = hulls[c + 1] - i * l; break; } } selections = new int[n]; for (int i = 0; i < n; ++i) { selections[i] = hulls[cursors[i]] - i * l; } }
// effects with the same cooldown and the same nonnegative bucket number will not be triggered simultaneously // otherwise, we naively engage each cooldown ASAP public static FightDivision ComputeNaive(SpecialEffect[] effects, double fightLength, int[] buckets = null) { KeyValuePair <SpecialEffect, int>[] effectsAndBuckets = new KeyValuePair <SpecialEffect, int> [effects.Length]; for (int i = 0; i < effects.Length; ++i) { effectsAndBuckets[i] = new KeyValuePair <SpecialEffect, int>(effects[i], (buckets != null) ? buckets[i] : -1); } Array.Sort(effectsAndBuckets, new SpecialEffectComparer()); double lastCooldown = double.PositiveInfinity; int lastBucket = -1; double bucketOffset = 0; DoubleMaxHeap <int> pq = new DoubleMaxHeap <int>(); for (int i = 0; i < effectsAndBuckets.Length; ++i) { effects[i] = effectsAndBuckets[i].Key; if (effects[i].Cooldown != lastCooldown) { lastCooldown = effects[i].Cooldown; lastBucket = -1; bucketOffset = 0; } if (effectsAndBuckets[i].Value != lastBucket) { lastBucket = effectsAndBuckets[i].Value; bucketOffset = 0; } double offset = bucketOffset; if (lastBucket >= 0) { bucketOffset += effects[i].Duration; } pq.Push(-offset, i << 1); } pq.Push(-fightLength, -1); Dictionary <int, double> effectDurations = new Dictionary <int, double>(); int effectMask = 0; double time = 0; for (; ;) { double nextTime = -pq.TopKey; int id = pq.TopValue; pq.Pop(); if (nextTime != time) { double v = 0; effectDurations.TryGetValue(effectMask, out v); effectDurations[effectMask] = v + nextTime - time; } time = nextTime; if (id < 0) { break; } int i = id >> 1; if ((id & 1) == 0) { effectMask |= 1 << i; pq.Push(-(time + effects[i].Duration), (i << 1) | 1); } else { effectMask &= ~(1 << i); pq.Push(-(time + effects[i].Cooldown - effects[i].Duration), i << 1); } } List <KeyValuePair <int, double> > effectDurationsList = new List <KeyValuePair <int, double> >(); effectDurationsList.AddRange(effectDurations); effectDurationsList.Sort((a, b) => a.Key - b.Key); int[] effectMasks = new int[effectDurations.Count]; double[] fractions = new double[effectDurations.Count]; { int i = 0; foreach (KeyValuePair <int, double> effectDuration in effectDurationsList) { effectMasks[i] = effectDuration.Key; fractions[i] = effectDuration.Value / fightLength; ++i; } } return(new FightDivision(effects, fractions, effectMasks)); }
/* PROBLEM DESCRIPTION: * We have a set budget, that we need to split between agents who can divide their time between a set of actions, to maximize the resulting gain. * * OUTPUT: * selections[] is the selected action for each agent * if interpSet is >= 0, then it denotes the only agent that splits its time between actions: (1 - interpT) time doing selections[interpSet] and interpT time doing interpTarget * * NOTE: will clobber costs and gains arrays */ public static void FindBestBudgetSplit(double[] costs, double[] gains, int l, int n, double budget, out int[] selections, out int interpSet, out double interpT, out int interpTarget) { int[] hulls = new int[n * l]; int[] hullends = new int[n]; double[] scratch = new double[l]; for (int i = 0; i < n; ++i) hullends[i] = i * l + FindUpperIncreasingConvexHullPoints(costs, gains, hulls, i * l, l, scratch); DoubleMaxHeap<int> pq = new DoubleMaxHeap<int>(); int[] cursors = new int[n]; for (int i = 0; i < n; ++i) { int c = i * l; cursors[i] = c; budget -= costs[c]; if ((c + 1) < hullends[i]) pq.Push((gains[c + 1] - gains[c]) / (costs[c + 1] - costs[c]), i); } interpSet = -1; interpT = 0; interpTarget = -1; while (pq.Count > 0 && budget > 0) { int i = pq.TopValue; pq.Pop(); int c = cursors[i]; double deltacost = costs[c + 1] - costs[c]; if (deltacost < budget) { budget -= deltacost; cursors[i] = ++c; if ((c + 1) < hullends[i]) pq.Push((gains[c + 1] - gains[c]) / (costs[c + 1] - costs[c]), i); } else { interpSet = i; interpT = budget / deltacost; interpTarget = hulls[c + 1] - i * l; break; } } selections = new int[n]; for (int i = 0; i < n; ++i) selections[i] = hulls[cursors[i]] - i * l; }