Bomb dropping algorithm

后端 未结 30 878
挽巷
挽巷 2021-01-29 16:55

I have an n x m matrix consisting of non-negative integers. For example:

2 3 4 7 1
1 5 2 6 2
4 3 4 2 1
2 1 2 4 1
3 1 3 4 1
2 1 4 3 2
6 9 1 6 4
         


        
30条回答
  •  无人共我
    2021-01-29 17:45

    I can't think of a way to calculate the actual number without just computing the bombing campaign using my best heuristic and hope I get a reasonable result.

    So my method is to compute a bombing efficiency metric for each cell, bomb the cell with the highest value, .... iterate the process until I've flattened everything. Some have advocated using simple potential damage (i.e. score from 0 to 9) as a metric, but that falls short by pounding high value cells and not making use of damage overlap. I'd calculate cell value - sum of all neighbouring cells, reset any positive to 0 and use the absolute value of anything negative. Intuitively this metric should make a selection that help maximise damage overlap on cells with high counts instead of pounding those directly.

    The code below reaches total destruction of the test field in 28 bombs (note that using potential damage as metric yields 31!).

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace StackOverflow
    {
      internal class Program
      {
        // store the battle field as flat array + dimensions
        private static int _width = 5;
        private static int _length = 7;
        private static int[] _field = new int[] {
            2, 3, 4, 7, 1,
            1, 5, 2, 6, 2,
            4, 3, 4, 2, 1,
            2, 1, 2, 4, 1,
            3, 1, 3, 4, 1,
            2, 1, 4, 3, 2,
            6, 9, 1, 6, 4
        };
        // this will store the devastation metric
        private static int[] _metric;
    
        // do the work
        private static void Main(string[] args)
        {
            int count = 0;
    
            while (_field.Sum() > 0)
            {
                Console.Out.WriteLine("Round {0}:", ++count);
                GetBlastPotential();
                int cell_to_bomb = FindBestBombingSite();
                PrintField(cell_to_bomb);
                Bomb(cell_to_bomb);
            }
            Console.Out.WriteLine("Done in {0} rounds", count);
        } 
    
        // convert 2D position to 1D index
        private static int Get1DCoord(int x, int y)
        {
            if ((x < 0) || (y < 0) || (x >= _width) || (y >= _length)) return -1;
            else
            {
                return (y * _width) + x;
            }
        }
    
        // Convert 1D index to 2D position
        private static void Get2DCoord(int n, out int x, out int y)
        {
            if ((n < 0) || (n >= _field.Length))
            {
                x = -1;
                y = -1;
            }
            else
            {
                x = n % _width;
                y = n / _width;
            }
        }
    
        // Compute a list of 1D indices for a cell neighbours
        private static List GetNeighbours(int cell)
        {
            List neighbours = new List();
            int x, y;
            Get2DCoord(cell, out x, out y);
            if ((x >= 0) && (y >= 0))
            {
                List tmp = new List();
                tmp.Add(Get1DCoord(x - 1, y - 1));
                tmp.Add(Get1DCoord(x - 1, y));
                tmp.Add(Get1DCoord(x - 1, y + 1));
                tmp.Add(Get1DCoord(x, y - 1));
                tmp.Add(Get1DCoord(x, y + 1));
                tmp.Add(Get1DCoord(x + 1, y - 1));
                tmp.Add(Get1DCoord(x + 1, y));
                tmp.Add(Get1DCoord(x + 1, y + 1));
    
                // eliminate invalid coords - i.e. stuff past the edges
                foreach (int c in tmp) if (c >= 0) neighbours.Add(c);
            }
            return neighbours;
        }
    
        // Compute the devastation metric for each cell
        // Represent the Value of the cell minus the sum of all its neighbours
        private static void GetBlastPotential()
        {
            _metric = new int[_field.Length];
            for (int i = 0; i < _field.Length; i++)
            {
                _metric[i] = _field[i];
                List neighbours = GetNeighbours(i);
                if (neighbours != null)
                {
                    foreach (int j in neighbours) _metric[i] -= _field[j];
                }
            }
            for (int i = 0; i < _metric.Length; i++)
            {
                _metric[i] = (_metric[i] < 0) ? Math.Abs(_metric[i]) : 0;
            }
        }
    
        //// Compute the simple expected damage a bomb would score
        //private static void GetBlastPotential()
        //{
        //    _metric = new int[_field.Length];
        //    for (int i = 0; i < _field.Length; i++)
        //    {
        //        _metric[i] = (_field[i] > 0) ? 1 : 0;
        //        List neighbours = GetNeighbours(i);
        //        if (neighbours != null)
        //        {
        //            foreach (int j in neighbours) _metric[i] += (_field[j] > 0) ? 1 : 0;
        //        }
        //    }            
        //}
    
        // Update the battle field upon dropping a bomb
        private static void Bomb(int cell)
        {
            List neighbours = GetNeighbours(cell);
            foreach (int i in neighbours)
            {
                if (_field[i] > 0) _field[i]--;
            }
        }
    
        // Find the best bombing site - just return index of local maxima
        private static int FindBestBombingSite()
        {
            int max_idx = 0;
            int max_val = int.MinValue;
            for (int i = 0; i < _metric.Length; i++)
            {
                if (_metric[i] > max_val)
                {
                    max_val = _metric[i];
                    max_idx = i;
                }
            }
            return max_idx;
        }
    
        // Display the battle field on the console
        private static void PrintField(int cell)
        {
            for (int x = 0; x < _width; x++)
            {
                for (int y = 0; y < _length; y++)
                {
                    int c = Get1DCoord(x, y);
                    if (c == cell)
                        Console.Out.Write(string.Format("[{0}]", _field[c]).PadLeft(4));
                    else
                        Console.Out.Write(string.Format(" {0} ", _field[c]).PadLeft(4));
                }
                Console.Out.Write(" || ");
                for (int y = 0; y < _length; y++)
                {
                    int c = Get1DCoord(x, y);
                    if (c == cell)
                        Console.Out.Write(string.Format("[{0}]", _metric[c]).PadLeft(4));
                    else
                        Console.Out.Write(string.Format(" {0} ", _metric[c]).PadLeft(4));
                }
                Console.Out.WriteLine();
            }
            Console.Out.WriteLine();
        }           
      }
    }
    

    The resulting bombing pattern is output as follows (field values on the left, metric on the right)

    Round 1:
      2   1   4   2   3   2   6  ||   7  16   8  10   4  18   6
      3   5   3   1   1   1   9  ||  11  18  18  21  17  28   5
      4  [2]  4   2   3   4   1  ||  19 [32] 21  20  17  24  22
      7   6   2   4   4   3   6  ||   8  17  20  14  16  22   8
      1   2   1   1   1   2   4  ||  14  15  14  11  13  16   7
    
    Round 2:
      2   1   4   2   3   2   6  ||   5  13   6   9   4  18   6
      2   4   2   1   1  [1]  9  ||  10  15  17  19  17 [28]  5
      3   2   3   2   3   4   1  ||  16  24  18  17  17  24  22
      6   5   1   4   4   3   6  ||   7  14  19  12  16  22   8
      1   2   1   1   1   2   4  ||  12  12  12  10  13  16   7
    
    Round 3:
      2   1   4   2   2   1   5  ||   5  13   6   7   3  15   5
      2   4   2   1   0   1   8  ||  10  15  17  16  14  20   2
      3  [2]  3   2   2   3   0  ||  16 [24] 18  15  16  21  21
      6   5   1   4   4   3   6  ||   7  14  19  11  14  19   6
      1   2   1   1   1   2   4  ||  12  12  12  10  13  16   7
    
    Round 4:
      2   1   4   2   2   1   5  ||   3  10   4   6   3  15   5
      1   3   1   1   0   1   8  ||   9  12  16  14  14  20   2
      2   2   2   2   2  [3]  0  ||  13  16  15  12  16 [21] 21
      5   4   0   4   4   3   6  ||   6  11  18   9  14  19   6
      1   2   1   1   1   2   4  ||  10   9  10   9  13  16   7
    
    Round 5:
      2   1   4   2   2   1   5  ||   3  10   4   6   2  13   3
      1   3   1   1   0  [0]  7  ||   9  12  16  13  12 [19]  2
      2   2   2   2   1   3   0  ||  13  16  15  10  14  15  17
      5   4   0   4   3   2   5  ||   6  11  18   7  13  17   6
      1   2   1   1   1   2   4  ||  10   9  10   8  11  13   5
    
    Round 6:
      2   1   4   2   1   0   4  ||   3  10   4   5   2  11   2
      1   3   1   1   0   0   6  ||   9  12  16  11   8  13   0
      2   2   2   2   0   2   0  ||  13  16  15   9  14  14  15
      5   4  [0]  4   3   2   5  ||   6  11 [18]  6  11  15   5
      1   2   1   1   1   2   4  ||  10   9  10   8  11  13   5
    
    Round 7:
      2   1   4   2   1   0   4  ||   3  10   4   5   2  11   2
      1   3   1   1   0   0   6  ||   8  10  13   9   7  13   0
      2  [1]  1   1   0   2   0  ||  11 [15] 12   8  12  14  15
      5   3   0   3   3   2   5  ||   3   8  10   3   8  15   5
      1   1   0   0   1   2   4  ||   8   8   7   7   9  13   5
    
    Round 8:
      2   1   4   2   1   0   4  ||   1   7   2   4   2  11   2
      0   2   0   1   0   0   6  ||   7   7  12   7   7  13   0
      1   1   0   1   0   2   0  ||   8   8  10   6  12  14  15
      4   2   0   3   3  [2]  5  ||   2   6   8   2   8 [15]  5
      1   1   0   0   1   2   4  ||   6   6   6   7   9  13   5
    
    Round 9:
      2   1   4   2   1   0   4  ||   1   7   2   4   2  11   2
      0   2   0   1   0   0   6  ||   7   7  12   7   6  12   0
      1   1   0   1   0  [1]  0  ||   8   8  10   5  10 [13] 13
      4   2   0   3   2   2   4  ||   2   6   8   0   6   9   3
      1   1   0   0   0   1   3  ||   6   6   6   5   8  10   4
    
    Round 10:
      2   1   4   2   1   0   4  ||   1   7   2   4   2  10   1
      0   2  [0]  1   0   0   5  ||   7   7 [12]  7   6  11   0
      1   1   0   1   0   1   0  ||   8   8  10   4   8   9  10
      4   2   0   3   1   1   3  ||   2   6   8   0   6   8   3
      1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2
    
    Round 11:
      2   0   3   1   1   0   4  ||   0   6   0   3   0  10   1
      0   1   0   0   0  [0]  5  ||   4   5   5   5   3 [11]  0
      1   0   0   0   0   1   0  ||   6   8   6   4   6   9  10
      4   2   0   3   1   1   3  ||   1   5   6   0   5   8   3
      1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2
    
    Round 12:
      2   0   3   1   0   0   3  ||   0   6   0   2   1   7   1
      0   1   0   0   0   0   4  ||   4   5   5   4   1   7   0
      1   0   0   0   0  [0]  0  ||   6   8   6   4   5  [9]  8
      4   2   0   3   1   1   3  ||   1   5   6   0   4   7   2
      1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2
    
    Round 13:
      2   0   3   1   0   0   3  ||   0   6   0   2   1   6   0
      0   1   0   0   0   0   3  ||   4   5   5   4   1   6   0
      1  [0]  0   0   0   0   0  ||   6  [8]  6   3   3   5   5
      4   2   0   3   0   0   2  ||   1   5   6   0   4   6   2
      1   1   0   0   0   1   3  ||   6   6   6   3   4   4   0
    
    Round 14:
      2   0   3   1   0  [0]  3  ||   0   5   0   2   1  [6]  0
      0   0   0   0   0   0   3  ||   2   5   4   4   1   6   0
      0   0   0   0   0   0   0  ||   4   4   4   3   3   5   5
      3   1   0   3   0   0   2  ||   0   4   5   0   4   6   2
      1   1   0   0   0   1   3  ||   4   4   5   3   4   4   0
    
    Round 15:
      2   0   3   1   0   0   2  ||   0   5   0   2   1   4   0
      0   0   0   0   0   0   2  ||   2   5   4   4   1   4   0
      0   0   0   0   0   0   0  ||   4   4   4   3   3   4   4
      3   1   0   3   0  [0]  2  ||   0   4   5   0   4  [6]  2
      1   1   0   0   0   1   3  ||   4   4   5   3   4   4   0
    
    Round 16:
      2  [0]  3   1   0   0   2  ||   0  [5]  0   2   1   4   0
      0   0   0   0   0   0   2  ||   2   5   4   4   1   4   0
      0   0   0   0   0   0   0  ||   4   4   4   3   3   3   3
      3   1   0   3   0   0   1  ||   0   4   5   0   3   3   1
      1   1   0   0   0   0   2  ||   4   4   5   3   3   3   0
    
    Round 17:
      1   0   2   1   0   0   2  ||   0   3   0   1   1   4   0
      0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
      0   0   0   0   0   0   0  ||   4   4   4   3   3   3   3
      3   1  [0]  3   0   0   1  ||   0   4  [5]  0   3   3   1
      1   1   0   0   0   0   2  ||   4   4   5   3   3   3   0
    
    Round 18:
      1   0   2   1   0   0   2  ||   0   3   0   1   1   4   0
      0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
      0   0   0   0   0   0   0  ||   3   3   2   2   2   3   3
      3  [0]  0   2   0   0   1  ||   0  [4]  2   0   2   3   1
      1   0   0   0   0   0   2  ||   2   4   2   2   2   3   0
    
    Round 19:
      1   0   2   1   0  [0]  2  ||   0   3   0   1   1  [4]  0
      0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
      0   0   0   0   0   0   0  ||   2   2   2   2   2   3   3
      2   0   0   2   0   0   1  ||   0   2   2   0   2   3   1
      0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0
    
    Round 20:
      1  [0]  2   1   0   0   1  ||   0  [3]  0   1   1   2   0
      0   0   0   0   0   0   1  ||   1   3   3   3   1   2   0
      0   0   0   0   0   0   0  ||   2   2   2   2   2   2   2
      2   0   0   2   0   0   1  ||   0   2   2   0   2   3   1
      0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0
    
    Round 21:
      0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
      0   0   0   0   0   0   1  ||   0   1   2   2   1   2   0
      0   0   0   0   0   0   0  ||   2   2   2   2   2   2   2
      2   0   0   2   0  [0]  1  ||   0   2   2   0   2  [3]  1
      0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0
    
    Round 22:
      0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
      0   0   0   0   0   0   1  ||   0   1   2   2   1   2   0
     [0]  0   0   0   0   0   0  ||  [2]  2   2   2   2   1   1
      2   0   0   2   0   0   0  ||   0   2   2   0   2   1   1
      0   0   0   0   0   0   1  ||   2   2   2   2   2   1   0
    
    Round 23:
      0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
      0   0  [0]  0   0   0   1  ||   0   1  [2]  2   1   2   0
      0   0   0   0   0   0   0  ||   1   1   2   2   2   1   1
      1   0   0   2   0   0   0  ||   0   1   2   0   2   1   1
      0   0   0   0   0   0   1  ||   1   1   2   2   2   1   0
    
    Round 24:
      0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
      0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
      0   0  [0]  0   0   0   0  ||   1   1  [2]  2   2   1   1
      1   0   0   2   0   0   0  ||   0   1   2   0   2   1   1
      0   0   0   0   0   0   1  ||   1   1   2   2   2   1   0
    
    Round 25:
      0   0   0   0   0  [0]  1  ||   0   0   0   0   0  [2]  0
      0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
      0   0   0   0   0   0   0  ||   1   1   1   1   1   1   1
      1   0   0   1   0   0   0  ||   0   1   1   0   1   1   1
      0   0   0   0   0   0   1  ||   1   1   1   1   1   1   0
    
    Round 26:
      0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
      0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
     [0]  0   0   0   0   0   0  ||  [1]  1   1   1   1   0   0
      1   0   0   1   0   0   0  ||   0   1   1   0   1   1   1
      0   0   0   0   0   0   1  ||   1   1   1   1   1   1   0
    
    Round 27:
      0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
      0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
      0   0  [0]  0   0   0   0  ||   0   0  [1]  1   1   0   0
      0   0   0   1   0   0   0  ||   0   0   1   0   1   1   1
      0   0   0   0   0   0   1  ||   0   0   1   1   1   1   0
    
    Round 28:
      0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
      0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
      0   0   0   0   0   0   0  ||   0   0   0   0   0   0   0
      0   0   0   0   0  [0]  0  ||   0   0   0   0   0  [1]  1
      0   0   0   0   0   0   1  ||   0   0   0   0   0   1   0
    
    Done in 28 rounds
    

提交回复
热议问题