吴昊品游戏核心算法(新春特别篇)—— 吴昊教你玩泡泡龙(模拟)(HDOJ 1547)

泡泡龙(パズルボブル,Puzzle Bobble,Bust-a-Move),TAITO在1994年首次发布,是一个十分流行的弹珠游戏。从下方中央的弹珠发射台射出彩珠,多于3个同色珠相连则会消失。也支持双人游戏,不过此模式下,两个玩家互相竞争:一个消除大片泡泡会转移到对方阵地。

  (Source:HDOJ 1547)这里有如下两个假设,一个假设是关于泡泡的消除的解释,另一个假设有些BT,我们假设所有的残局都在于游戏界面的左上角,也就是说,射击的时候(假设为直线射击,并且包括反弹,是可以击中所有的外表面层的泡泡)

   泡泡消除的方式:

   主要有爆掉和下坠两种方式,前者是后者的基础。

  (a) 泡泡爆掉

  当玩家主动令游戏区形成 3 个或以上的同色泡泡相连,则这些泡泡将爆掉。 

  (b) 泡泡下坠

  当爆掉的泡泡造成连接的异色泡泡失去挂点,则这些异色泡泡将下坠,算入消去的泡泡中。(下坠中的泡泡不对其他泡泡发生作用)

 

  输入和输出的格式如下,前面两个数字表示泡泡所在区域的高度和宽度,而后面的坐标(2,1)表示最新的一个泡泡打出的位置,位置的标记中,x轴是从上往下扫描的,而y轴是从左到右扫描的,假设起点都是1(有a到z一共26种泡泡,标识空缺的位置可以考虑大写字母E)

  Example:

  Input:

   2 2 2 1

   aa

   a

  输出则为一个数字,表示最新的一个泡泡所产生的效应,就是令多少泡泡下落了,这里需要考虑上述所说的(a)和(b)两种方式,如果Input为上面所说的话,则Output如下:

  Output:

  3

 

  以下我给出两种搜索方法,一种是深度优先搜索(DFS),一种是广度优先搜索(BFS),各有各的特点:

  (一)DFS方法:

   先由新加入点开始深搜,计算出相连且颜色相同的点块中点的个数,同时经过的点置0。如果点个数小于3则输出0,反之再枚举顶端的点,从每个点出发深搜(不 能经过置0的点),最后图中剩下没有置0的点就是会掉下去的点,计数即可。用到的一个技巧为方向数组dir[6][2]中的(0,2)和(0,-2),这 里特别地将Y轴的步进坐标翻倍。

 

 1 #include<cstdio>
 2  #include<cstring>
 3  //C++由于包含C,故既可以直接调用标准库,也可以采用这种cstring,cstdio的形式
 4  using namespace std;
 5  
 6  //深度搜索的方向
 7  int dir[6][2]={-1,1,0,2,1,1,1,-1,0,-2,-1,-1};
 8  char mat[205][205];
 9  int n,m,x,y,maxx;
10  
11  void dfs(int a,int b,char c)
12  {
13     int p,q;
14     //这个是初始点
15     maxx++;
16     mat[a][b]=0;
17     for(int i=0;i<6;++i)
18     {
19         p=a+dir[i][0];q=b+dir[i][1];
20         if(0<=a&&a<=n&&0<=b&&b<=m&&mat[p][q])
21         {
22             if(mat[p][q]==c)
23                dfs(p,q,c);
24             if(c=='0')
25                dfs(p,q,c);
26         }
27     }
28 }
29 
30  int main()
31  {
32    int ans;
33    //while (~scanf(..) ) == while (scanf() != EOF):~scanf这种用法不常用,等效于这种用法
34    for(;~scanf("%d%d%d%d",&n,&m,&x,&y);)
35    {
36      //对mat二维数组初始化
37      memset(mat,0,sizeof(mat));
38      ans=maxx=0;
39      for(int i=1;i<=n;++i)
40        scanf("%s",mat[i]);
41      for(int i=1;i<=n;i+=2)
42        for(int j=m-1;j>=0;--j)
43        {
44          if(mat[i][j]=='E')  mat[i][j]=0;
45          mat[i][j*2+1]=mat[i][j];
46          mat[i][j]=0;
47        }
48      for(int i=2;i<=n;i+=2)
49        for(int j=m-2;j>=0;--j)
50        {
51          if(mat[i][j]=='E')  mat[i][j]=0;
52          mat[i][j*2+2]=mat[i][j];
53          mat[i][j]=0;
54        }
55      //这里是为了配合搜索的方向,将y轴翻倍
56      m=2*m;
57      if(x&1) y=(y-1)*2+1;
58      else    y=(y-1)*2+2;
59      //先从最新消除的那个点开始dfs
60      dfs(x,y,mat[x][y]);
61      ans=maxx;
62      if(ans<3)
63        ans=0;
64      else
65      {
66        for(int i=1;i<=m;++i)
67        //如果可以有消去的话,从顶段的每一列挨个dfs
68        if(mat[1][i])
69          dfs(1,i,'0');
70        //都搜索完之后,对每一个点扫描,图中剩下的没有置0的点就是掉落的点
71        for(int i=1;i<=n;++i)
72          for(int j=1;j<=m;++j)
73            if(mat[i][j]) ans++;
74        printf("%d\n",ans);
75      }
76    }
77    return 0;
78 }

  (二)BFS方法:

   思想和DFS方法一样,这里只是修改了一点,就是先计算出所有气球的数目,第一个Bfs只是为了检验一下是否可以消去气球,顺便标记所有同类型那一片气 球,在第二个Bfs也就是Bfs2函数中,挨个挨个从顶段往下扫描,记录不可能被消去的小球的数目(这里包含了泡泡下坠的这种情况,因为如果顶段的泡泡为 相同类型的话,其下面的点将很有可能(因为这里还有可能存在从其余的顶端将那一端连成片的情况)一起下坠,而不管是否是同类型的)。最后,做出一个减法即可。

 

  1  #include<iostream>
  2  #include<cstdio>
  3  #include<string>
  4  #include<queue> //对STL中queue的使用
  5  using namespace std;
  6  
  7  //设置气泡结构体的实例Node
  8  typedef struct node
  9  {
 10    int x;
 11    int y;
 12  }Node;
 13 
 14  int map[101][101];//标记气球颜色
 15  int mark[101][101];//
 16  int h,w,bi,bj,temp;
 17  int mark2[101][101],sum2;
 18  int dic[6][2];
 19 
 20  void fun(int n)
 21  {
 22    if(n % 2 == 1)
 23    {
 24      dic[0][0] = -1;  dic[0][1] = 0;
 25      dic[1][0] = 1;   dic[1][1] = 0;
 26      dic[2][0] = 0;   dic[2][1] = -1;
 27      dic[3][0] = 0;   dic[3][1] = 1;
 28      dic[4][0] = -1;  dic[4][1] = -1;
 29      dic[5][0] = 1;   dic[5][1] = -1;//六个方向,上下左右,左下,左上
 30    }
 31    else
 32    {
 33      dic[0][0] = -1;  dic[0][1] = 0;
 34      dic[1][0] = 1;   dic[1][1] = 0;
 35      dic[2][0] = 0;   dic[2][1] = -1;
 36      dic[3][0] = 0;   dic[3][1] = 1;
 37      dic[4][0] = -1;  dic[4][1] = 1;
 38      dic[5][0] = 1;   dic[5][1] = 1;//六个方向右上,右下
 39    }
 40  }
 41 
 42  void Bfs2(int x,int y)//广搜与最上层相连的气球
 43  {
 44    int k;
 45    queue<Node> Q;
 46    Node q,p;
 47    p.x = x;
 48    p.y = y;
 49    Q.push(p);
 50    //同理,对第一行的每一列,逐个标记为已确认点
 51    mark2[x][y] = 1;
 52    sum2++;
 53    while(!Q.empty())
 54    {
 55      q = Q.front();
 56      Q.pop();
 57      fun(q.x);
 58      for(k = 0;k < 6;k++)
 59      {
 60        p.x = q.x + dic[k][0];
 61        p.y = q.y + dic[k][1];
 62        if(p.x>0 && p.x<=h && p.y>0 && p.y<=w && mark2[p.x][p.y]==0 && mark[p.x][p.y]==0)
 63        {
 64          mark2[p.x][p.y] = 1;
 65          sum2++;
 66          Q.push(p);
 67        }
 68      }
 69    }
 70  }
 71 
 72 
 73  void Bfs()//广搜可以直接爆炸的气球
 74  {
 75    int k,sum;
 76    queue<Node> Q;
 77    Node p,q;
 78    p.x = bi;
 79    p.y = bj;
 80    Q.push(p);
 81    //每进入一个值,就将其置为空
 82    mark[bi][bj] = 1;
 83    sum = 1;
 84    while(!Q.empty())
 85    {
 86      q = Q.front();
 87      Q.pop();
 88      //对奇数行和偶数行分别采用不同的搜索方式
 89      fun(q.x);
 90      for(k = 0;k < 6;k++)
 91      {
 92        p.x = q.x + dic[k][0];
 93        p.y = q.y + dic[k][1];
 94        //如果格子不为空,切泡泡为同类型的话
 95        if(p.x > 0 && p.x <= h && p.y > 0 && p.y <= w && mark[p.x][p.y] == 0 && map[p.x][p.y] == map[bi][bj])
 96        {
 97          //将其标记为空
 98          mark[p.x][p.y] = 1;
 99          sum++;
100          Q.push(p);
101        }
102      }
103    }
104    if(sum >= 3)
105    {
106      sum2 = 0;//记录与最上层相连的气球数
107      for(k = 1; k <= w;k++)
108      {
109        //逐个搜索顶层还没有标记,这里需要考虑的是还可以保住多少个气球不掉落,因为如果最上层为同类型的话,其下面的不管是否为同类都要消去了
110        if(mark[1][k] == 0 && mark2[1][k] == 0)
111          Bfs2(1,k);
112      }
113      sum = temp - sum2;
114    }
115    //如果少于3个泡泡的话,是不能进行消去的,这里退出
116    else
117      sum = 0;
118    cout<<sum<<endl;
119  }
120 
121  int main()
122  {
123    while(scanf("%d%d%d%d",&h,&w,&bi,&bj) != EOF)
124    {
125      int i,j;
126      string str;
127      //读入数据
128      for(i = 1;i <= h;i++)
129      {
130        for(j = 1;j <= w;j++)
131        {
132          map[i][j] = 0;
133          mark[i][j] = 1;//表示没有气球
134          mark2[i][j] = 0;//未被访问过
135        }
136      }
137      temp = 0;//记录总的气球数
138      for(i = 1;i <= h;i++)
139      {
140        //读入一行
141        cin>>str;
142        //调整坐标,根据泡泡龙游戏的规则,每一行的开始会空余一格(这里可以详见泡泡龙游戏的具体图片)
143        if(i%2 ==0)
144          str += " ";
145        //空的为什么不管?因为在开始的时候就假定所有的气球都为空了
146        for(j = 0;j < str.length();j++)
147        {
148          if(str[j] >='a' && str[j] <= 'z')
149          {
150            map[i][j+1] = str[j]-'a'+1;//标记球的颜色
151            mark[i][j+1] = 0;//表示气球的确存在
152            temp++;
153          }
154        }
155      }
156      //如果新打入的那一点为空的话,就不会有任何的消除,因为不能产生任何的变化
157      if(mark[bi][bj] == 1)
158      {
159        cout<<"0"<<endl;
160        continue;
161      }
162      Bfs();
163    }
164    return 0;
165  }

 

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

导航