吴昊品游戏核心算法(新春特别篇)—— 吴昊教你玩泡泡龙(模拟)(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轴的步进坐标翻倍。
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函数中,挨个挨个从顶段往下扫描,记录不可能被消去的小球的数目(这里包含了泡泡下坠的这种情况,因为如果顶段的泡泡为 相同类型的话,其下面的点将很有可能(因为这里还有可能存在从其余的顶端将那一端连成片的情况)一起下坠,而不管是否是同类型的)。最后,做出一个减法即可。
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 }
浙公网安备 33010602011771号