和吴昊一起玩推理 Round 5 —— 战车问题(蒙特卡洛实战)

如图所示,以上是一个N*N的方格,上面布满了堡垒和坦克。其中,堡垒用来防御而坦克用来攻击,这当然只是两种不同的行为而已。这里假设坦克是不能越过堡 垒的,同时假设堡垒过于强大,以至于坦克无法利用炮轰的方式将其炸毁。那么,我们假设战车各自为派(那么,可以想见,在没有任何的堡垒的情况下,方格上最 多有N辆战车),希望我们在初始阶段尽可能地放置更多的互相不受攻击的战车。这也是许多坦克游戏中的初始布局的基本要求,因为,如果一开始你就可以攻击到 对方,这样有失游戏的公平性。

  

   这是一个坦克游戏的残局,在布局阶段,是不允许双方的坦克受到彼此之间的攻击的。

 

  【分析】从第一行开始随机产生一个位置,看战车在该位置上能否放置,分三种情况讨论:

    a. 该随机位置对应在棋盘上是一个堡垒,则不能放置;

    b. 该位置与前面放置的战车相冲突,即在受前面放置战车攻击的位置上,则

    也不能放置;

    c. 该随机位置不受攻击也不是堡垒,则可以放置;

    如果不能放置,则重新产生一个随机位置再判断,如果可以放置, 则放在该位置上并记录战车个数和该战车可能攻击的位置。以这种方法从第一行开始放置,第一行不能放置战车后再放第二行,直至第n行结束。

 

  【设计】算法设计:

    a. 建立随机数类CRandomNumber;

    b. 函数CheckPlace判断是否可以放入战车,同时查看所放战车的攻击位置;

    c. 函数MaxChes产生随机位置,放置并记录战车数。

 

  1   /*
  2    Highlights:
  3      (1)采用随机数做种,这里的参数的选择,以及模拟实验的次数,是权衡正确度和时间效率的准则,需要人为想好
  4      (2)判断的时候,设置flag标志位,只有某一行的八个列全都标记了X的情况下才置flag为false,否则只要有一处有可能都不应该放过
  5      (3)每一行Check的时候,要将这一个战车的四周的"攻击点"都标记出来,同时,如果碰到了X,则停止标记
  6      (4)可以看到,这里即使是随机化方法,其中也包含着很多确定性的执着,这一点很有意思
  7      (5)如果模拟K次的话,该问题的时间复杂度为O(K*n^2)
  8  */
  9  
 10  #include<iostream.h>
 11  #include<fstream>
 12  #include<time.h>
 13  #include<stdlib.h>
 14  
 15  const unsigned long multiplier=1194211693L;
 16  const unsigned long adder=12345L;
 17  
 18  //随机数类
 19  class CRandomNum
 20  {
 21    private:
 22            unsigned long nSeed;
 23    public:
 24           CRandomNum(unsigned long s=0);
 25    unsigned short Random(unsigned long n);              
 26  }
 27  
 28  //用计算机的随机时间做种
 29  CRandomNum::CRandomNum(unsigned long s)
 30  {
 31    if(s==0)
 32      nSeed=time(0);
 33    else
 34      nSeed=s;                                
 35  }
 36  
 37  //对生成的种子做处理,使之符合具体问题的规模,这里采用(乘+加+取模)的形式
 38  unsigned short CRandomNum::Random(unsigned long n)
 39  {
 40    nSeed=multiplier*nSeed+adder;
 41    return (unsigned short)((nSeed>>16)%n);         
 42  }
 43  
 44  bool CheckPlaceChe(int **b,char **a,int x,int y,int n)
 45  {
 46    if(b[x][y]==1return false;
 47    else
 48    {
 49      //这里的置1不代表堡垒,而代表放置一个战车,这里,我们还要考虑放置之后对周围的格子的影响
 50      //这里,我们将战车四周的位置都标记为"受攻击点",直到有"X"标记的障碍物阻挡之后不再计数
 51      b[x][y]=1;
 52      if((y>=1)&&(y<=n))
 53      {
 54        for(int i=y-1;i>=0;i--)
 55        {
 56          if((b[x][i]==1)&&(a[x][i]=='X'))
 57            break;
 58          else
 59            b[x][i]=1;        
 60        }                  
 61      }    
 62      if((y<=n-1)&&(y>=0))
 63      {
 64        for(int i=y+1;i<n;i++)
 65        {
 66          if((b[x][i]==1)&&(a[x][i]=='X'))
 67            break;
 68          else
 69            b[x][i]=1;        
 70        }                    
 71      }
 72      if((x>=1)&&(x<=n))
 73      {
 74        for(int j=x-1;j>=0;j--)
 75        {
 76          if((b[j][y]==1)&&(a[j][y]=='X'))
 77            break;
 78          else b[j][y]=1;        
 79        }                  
 80      }
 81      if((x>=0)&&(x<=n-1))
 82      {
 83        for(int j=x-1;j<n;j++)
 84        {
 85          if((b[j][y]==1)&&(a[j][y]=='X'))
 86            break;
 87          else b[j][y]=1;        
 88        }                    
 89      }
 90      return true;
 91    }     
 92  }
 93  
 94  int MaxChes(int **b,char **a,int *x,int n)
 95  {
 96    int max1=0;
 97    //设置CRandomNum类中的静态实例
 98    static CRandomNum rnd;
 99    for(int i=0;i<n;i++)
100    {
101      bool flag=false;
102      do
103      {
104        for(int j=0;j<n;j++)
105        {
106          if(b[i][j]==0)
107          {
108            flag=true;
109            break;              
110          }       
111          //如果这个位置上面放置堡垒的话,是不能放置的,按照规则1
112          else
113          {
114            flag=false;
115            continue;    
116          }
117        }     
118        //如果"可能放置"的话,那么先生成一个在[0,n]之间的随机数,再利用CheckPlace函数检查一下这个位置是否可以放置
119        if(flag)
120        {
121          x[i]=rnd.Random(n);
122          if(CheckPlace(b,a,i,x[i],n))
123            max1++;        
124        }
125      }        
126      //利用do--while可以在执行命令之后再判断条件,符合这里的逻辑关系,当所有的随机情况都处理完后再退出
127      while(flag);
128    }    
129    return max1;
130  }
131  
132  int main()
133  {
134    int i,j,n;
135    cin>>n;
136    //开启两个二维数组,一个放数,一个放字符,b数组为a数组的"翻译数组",意思是如果a数组的某项存在碉堡,那么b数组在这个位置上需要置1
137    char **a=new char*[n];
138    for(i=0;i<n;i++)
139      a[i]=new char[n];
140    for(i=0;i<n;i++)
141      for(j=0;j<n;j++)
142        cin>>a[i][j];
143    int **b=new int*[n];
144    for(i=0;i<n;i++)
145      b[i]=new int[n];
146    int max=0;
147    //由于之后是对行进行扫描,这里开一个一维数组的目的也在于此
148    int *x=new int[n];
149    for(i=0;i<n;i++)
150      x[i]=0;
151    int t=0;
152    //实验15000次(具体的次数的选取需要参考设置的随机数生成器的参数)
153    while(t<15000)
154    {
155      for(int i=0;i<n;i++)
156      {
157        for(int j=0;j<n;j++)
158        {
159          if(a[i][j]=='X')
160            b[i][j]=1;
161          else
162            b[i][j]=0;        
163        }        
164      }              
165      //每一次,得到一个"可能的最大值"
166      int max2=MaxChes(b,a,x,n);
167      if(max<max2) max=max2;
168      t++;
169    }     
170    cout<<max;
171    //主动地丢弃数组内存,这是一个好的习惯,垃圾清理机制可是C++中没有的哦!
172    delete []x;
173    for(i=0;i<n;i++)
174      delete[] b[i];
175      delete[] b;
176    return 0;
177  }
178 
179 

posted on 2013-02-27 22:27  吴昊系列  阅读(372)  评论(0编辑  收藏  举报

导航