SRM 594, D2, 1000-Pointer (FoxAndGo2)

James S. Plank

Thu Oct 10 14:16:38 EDT 2019
This is a challenging problem to wrap your head around. As it turns out, it is most fruitful to think about the connected components than the individual cells. There are two types of connected components:
  1. White: These are components of white tokens.
  2. Empty: These are components of empty tokens, where you can put black tokens.
For example, look at the following board, on the left below:

ooo.
oooo
....
oooo
ooo.
0001
0000
2222
3333
3334

There are two white components and three empty components. Let's number them, as on the right above. We know a few things:

Now, let's define a concept of safety: An empty component E is safe with respect to a white component W if: You'll note that none of components 0, 2 or 4 are safe with respect to any of their adjacent components. However, I can tweak the board to give examples of components that are safe:

ooo..
oooo.
....o
oooo.
ooo..
00011
00001
22225
33334
33344

Now, components 1 and 4 are both safe with respect to the other components. Component 2 is interesting -- it is unsafe with respect to components 0 and 3; however it is safe with respect to component 5.

What does safety have to do with this problem? Well, this:

I hope that gives you a clue about how to solve this problem:

Here were my data structures:

class Cell {
  public:
    vector <Cell *> adj;          // Adjacency list (easier than using row/column)
    class Component *comp;        // My component
    int row;
    int col;
    int state;                    // '.', 'o'
    int number;                   // Each cell has a unique number - useful for computing component adjacency
};

class Component {
  public:
    int type;                     // '.' or 'o' 
    int number;                   // Unique across components
    vector <Cell *> cells;        // All of the cells in the component
    map <int, Component *> adj;   // Components to which I'm adjacenct.  Keyed by component number.
    map <int, int> adjcount;      // Keyed by component number - # of cells that we have adjacent.
    set <int> unsafe_neighbors;   // Only for white components.  Again, keyed on component number.
};

class FoxAndGo2 {
  public:
    int maxKill(vector <string> board);
    void DFS(Cell *c, Cell *start);           // For determining components
    vector <string> Board;                    // The board
    vector < vector <Cell *> > Cells;         // The cells in a grid
    vector <Cell *> AllCells;                 // All of the cells in a vector
    vector < Component * > White_Components;  // Just the white components.
    vector < Component * > Empty_Components;  // Just the empty components.
    vector < Component * > Components;        // All components
    deque < Component * > Killable;           // The killable components list.
    int Rows;
    int Cols;
};

Plus, I have annotated the output of my program; you can find outputs for each of the examples in the following links (abbreviations: WC = "White Component", UN = "Unsafe neighbor").