/* 2018/2019 Regional International Collegiate Programming Contest Problem ? Picking Up the Dice M. K. Furon. 2018-11-07. */ #include #include #include int bestcount, dicecount, difference, i, iocount, j, k, mindice; int targetnum, thrownsum; long long int bestodds, diceways; int die[25], selected[25]; long long int odds[25][150], power6[25]; void abend (char *message) { (void) fputs (message, stderr); (void) fputc ('\n', stderr); exit (4); } void checkrange (int value, int minvalue, int maxvalue, char *fault) { if ((value < minvalue) || (value > maxvalue)) { (void) fputs (fault, stderr); (void) fputs (": ", stderr); abend ("Input value out of range!"); } return; } void processdice (int k) { /* Select the dice in the "selected" array and look up the odds of re-rolling the selected dice hitting the target value. */ int needed, i; int totalselected = 0; long long int theseodds; for (i = 0; i < k; i++) totalselected += die[selected[i]]; needed = targetnum - (thrownsum - totalselected); if ((needed < k) || (needed > (6 * k))) return; theseodds = odds[k][needed]; if (theseodds > bestodds) { bestcount = k; bestodds = theseodds; } return; } /* next_comb(int comb[], int k, int n) Generates the next combination of n elements as k after comb comb => the previous combination ( use (0, 1, 2, ..., k) for first) k => the size of the subsets to generate n => the size of the original set Returns: 1 if a valid combination was found, 0, otherwise */ int next_comb(int comb[], int k, int n) { int i = k - 1; ++comb[i]; while ((i > 0) && (comb[i] >= n - k + 1 + i)) { --i; ++comb[i]; } if (comb[0] > n - k) /* If final combination reached */ return (0); /* comb now looks like (..., x, n, n, n, ..., n). Turn it into (..., x, x + 1, x + 2, ...) */ for (i = i + 1; i < k; ++i) comb[i] = comb[i - 1] + 1; return (1); } int main (int argc, char *argv[], char *envp[]) { /* Build the table of powers of six. */ power6[0] = 1LL; for (i = 1; i < 25; i++) power6[i] = 6LL * power6[(i - 1)]; /* Load the number of dice and the desired target number. */ iocount = scanf ("%d %d", &dicecount, &targetnum); if (iocount != 2) abend ("Missing initial parameters."); checkrange (dicecount, 2, 24, "Dice count"); checkrange (targetnum, dicecount, (dicecount * 6), "Target number"); odds[1][1] = odds[1][2] = odds[1][3] = odds[1][4] = odds[1][5] = odds[1][6] = 1LL; for (i = 2; i <= dicecount; i++) { /* Build the table of odds for rolling each possible number with a given number of dice. This is stored as 64-bit integers in order to handle up to 24 dice. */ for (j = i; j <= ((i * 7) / 2); j++) { diceways = 0LL; for (k = j - 1; ((k >= (j - 6)) && (k >= (i - 1))); k--) diceways += odds[(i - 1)][k]; odds[i][((i * 7) - j)] = odds[i][j] = diceways; } } for (i = 1; i <= dicecount; i++) { for (j = i; j <= ((i * 7) / 2); j++) { odds[i][((i * 7) - j)] = odds[i][j] = odds[i][j] * power6[(24 - i)]; } } /* Load the dice. */ thrownsum = 0; for (i = 0; i < dicecount; i++) { iocount = scanf ("%d", &(die[i])); if (iocount != 1) abend ("Missing die value."); thrownsum += die[i]; } /* Run through the possible combinations of (dicecount taken n at a time). */ if (thrownsum == targetnum) bestcount = 0; else { bestcount = 0; bestodds = 0LL; /* Determine the minimum number of dice needed based on the difference between the target number and the thrown value. */ difference = abs (targetnum - thrownsum); mindice = difference / 5; if ((mindice * 5) < difference) mindice++; for (k = mindice; k <= dicecount; k++) { for (i = 0; i < k; ++i) selected[i] = i; processdice (k); while (next_comb (selected, k, dicecount)) processdice (k); } } (void) printf ("%d\n", bestcount); return (0); }