주사위 고르기

Lv. 3

Solution

function combinations(n: number, k: number): number[][] {
  const result: number[][] = [];
 
  function backtrack(current: number[], start: number): void {
    if (current.length === k) {
      result.push([...current]);
      return;
    }
    for (let i = start; i < n; i++) {
      current.push(i);
      backtrack(current, i + 1);
      current.pop();
    }
  }
  backtrack([], 0);
  return result;
}
 
function getAllDiceResult(dices: number[][], indices: number[]): number[] {
  const result: number[] = [];
  function dfs(current: number, i: number): void {
    if (i === indices.length) {
      result.push(current);
      return;
    }
    for (const value of dices[indices[i]]) {
      dfs(current + value, i + 1);
    }
  }
  dfs(0, 0);
  return result.sort((a, b) => a - b);
}
 
function getKey(indices: number[]): number {
  return indices.reduce((prev, i) => prev + (1 << i), 0);
}
 
function getOpponentKey(n: number, key: number): number {
  return ((1 << n) - 1) ^ key;
}
 
function parseKey(n: number, key: number): number[] {
  const indices: number[] = [];
 
  for (let i = 0; i < n; i++) {
    if (key & (1 << i)) {
      indices.push(i);
    }
  }
  return indices;
}
 
function lowerbound(arr: number[], target: number): number {
  let [start, end] = [0, arr.length];
  while (start < end) {
    const mid = Math.floor((start + end) / 2);
    if (arr[mid] < target) {
      start = mid + 1;
    } else {
      end = mid;
    }
  }
  return start;
}
 
export function selectDice(dice: number[][]): number[] {
  const n = dice.length;
  const results = new Map<number, number[]>();
  for (const indices of combinations(n, n / 2)) {
    const key = getKey(indices);
    const result = getAllDiceResult(dice, indices);
    results.set(key, result);
  }
 
  let maxDices: number[] = [];
  let maxWins = 0;
  for (const [key, result] of results.entries()) {
    const opponentKey = getOpponentKey(n, key);
    const opponentResult = results.get(opponentKey) ?? [];
    let wins = 0;
    for (const value of result) {
      wins += lowerbound(opponentResult, value);
    }
 
    if (maxWins < wins) {
      maxWins = wins;
      maxDices = parseKey(n, key);
    }
  }
 
  return maxDices.map((i) => i + 1);
}