Pebble#10837

Pebble (Editorial)

Since the symbol appears at least 2 \cdot k - 1 times, the game will necessarily end in a win. The game would not end in a win only if each participant had at most k - 1 red stones. That would mean the total number of red stones in the game is at most 2k - 2, which contradicts the given condition.

Let's construct a prefix array in order to quickly determine the number of red stones on any subsegment [l, r]:

prefix[0] = (str[0] == 'C')
prefix[i] = prefix[i-1] + (str[i] == 'C')

Then the number of red stones on the subsegment [l, r] can be computed in time:

red_left = prefix[r];
if (l) red_left -= prefix[l-1];

Consider the following function:

int can_win(int l, int r, int uk)

Game termination conditions:

The players take turns making moves:

if (!can_win(l + 1, r, other_red) || !can_win(l, r - 1, other_red))
  res = 1;
else
  res = 0;

Here other\_red is the number of red stones that Branka will accumulate. In other words, we model the situation as: "I make a move, then the opponent makes a move". If there exists at least one move after which the opponent is not guaranteed to win, then Antun wins.

The value of other\_red is computed as follows:

int total_red = prefix[n - 1]; // total number of red stones in the string
int red_left = prefix[r];    // number of red stones up to position r
if (l) red_left -= prefix[l - 1]; // number of red stones in [l, r]
int other_red = total_red - red_left - uk;

Thus, the game is reduced to a recursive dynamic programming problem with three parameters:

Call the function and print the result:

if (can_win(0, n - 1, 0))
  puts("DA");  // Antun can guarantee a win
else
  puts("NE");  // no

Algorithm implementation

Let us declare the following arrays:

#define MAX 352
int prefix[MAX], dp[MAX][MAX][MAX];

The function can_win returns , if the current player (Antun or Branka) can guarantee a win from this position; otherwise, it returns .

int can_win(int l, int r, int uk)
{
  int& res = dp[l][r][uk];

If the value of the function has not been computed yet.

  if (res == -1)
  {

Compute how many red stones red\_left are in the interval [l, r];

Compute how many red stones total\_red - red\_left - uk will remain for the opponent (Branka / Antun), given that Antun / Branka has already collected .

    int total_red = prefix[n - 1];
    int red_left = prefix[r];
    if (l) red_left -= prefix[l - 1];
    int other_red = total_red - red_left - uk;

If uk \ge k, then Antun / Branka has already collected red stones, which means he /she loses.

    if (uk >= k)
      res = 0;

If Branka / Antun is guaranteed to collect red stones, then Antun / Branka wins.

    else if (other_red >= k)
      res = 1;
    else
    {

Antun / Branka can take a stone from the left (l + 1) or from the right (r - 1). In order for Antun to guarantee a win, at least one of his moves must lead to a position in which Branka / Antun does not have a winning strategy.

      if (!can_win(l + 1, r, other_red) || 
          !can_win(l, r - 1, other_red))
        res = 1;
      else
        res = 0;
    }
  }
  return res;
}

The main part of the program. Read the input data.

memset(dp, -1, sizeof(dp));
cin >> n >> k;
cin >> str;

Compute the prefix array prefix.

prefix[0] = (str[0] == 'C');
for (i = 1; i < n; i++) prefix[i] = prefix[i - 1] + (str[i] == 'C');

Call the function and print the result.

if (can_win(0, n - 1, 0))
  cout << "DA" << endl;
else
  cout << "NE" << endl;