写作缘由:腾讯马拉松第五场第三题----小明水滴游戏。参考http://blog.csdn.net/dyx404514/article/details/8720661,运用了BFS的思想,故重新阅读算法导论中广度优先树
写作目的:熟悉广度优先树,记忆其中的关键概念,能熟练写出其伪代码,并争取下次遇到含有BFS思想的题目时,能够更快想到BFS
参考资料《算法导论》
概念回顾
- 在给定图G(V,E)和一个
- 特定的源顶点s下,搜索所有的顶点,并计算s到达这所有顶点的剧烈,还能生成一个广度优先树
- 算法会首先发现和s距离为k的顶点,然后再搜索距离为k+1的顶点。
颜色变量存储于color中,包括三种颜色:黑色(完全探索到包括其相邻的节点)灰色(探索到,但是其相邻节点没有被全部探索到)白色(未探索到);一个点的父节点存储于∏[u]中;一个顶点被置为灰色时插入队列,而当顶点从队列出队列后,它将被置为黑色BFS( G,s) for each vertex u ∈V[G] -{s} do color[u] ← WHITE d[u] ← ∞ ∏[u] ← NIL color[s] ← GRAY d[s] ← 0 ∏s] ← NIL Q ← 空集 ENQUEUE(Q,s) while Q != 空集 do u ← DEQUEUE(Q) for each v ∈ Adj[u] do if color[v] = WHITE then color[v] ← GRAY d[v] ← d[u] +1 ∏[v] ← u ENQUEUE(Q,v) color[u] ← BLACK
- 过程BFS运行时间为O(V+E)。
- 当运行终止时,对于所有v,d[v]都是其到顶点的最短距离。
腾讯马拉松第五场第三题---小明水滴游戏
代码与题目
//Input
//题目包含多组测试用例;
//对于每组数据,首先是6行,每行有6个整数数字,每个数字的范围为0~4;当数字为0时,表示空格子,当数字为1~4时,表示1~4级的水滴;
//然后第七行是一个整数m,表示有m个操作;接下来是m行,每行有两个整数x, y ,表示在(x,y)放入一滴水。
//特别说明:每次都是在全部的水滴静止后才进行下一次操作,也就是说只有在方格内没有任何飞溅的小水滴时才能放入一滴水。
//
//[Technical Specification]
//1 <= m <= 10
//1 <= x, y <= 6
//
//
//Output
//对于每组测试数据,请输出m个操作之后6*6方格内水滴的样子,每组数据的输出后面跟着一个空行。
//
//
//Sample Input
//0 0 0 4 0 4
//0 0 0 0 0 0
//1 0 0 4 0 4
//0 0 0 0 0 0
//0 0 0 0 0 0
//0 0 0 1 0 0
//1
//1 6
//0 0 0 4 0 4
//0 2 0 4 0 4
//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 6
//2 2 0 2 0 3
//3 3 0 1 3 1
//2 2 2 4 0 4
//2 4 4 2 2 3
//3 3 2 4 0 1
//1 3 4 3 0 1
//6
//5 3
//5 3
//3 3
//3 2
//2 1
//6 3
//
//
//Sample Output
//0 0 0 0 0 0
//0 0 0 0 0 0
//2 0 0 0 0 0
//0 0 0 0 0 0
//0 0 0 0 0 0
//0 0 0 2 0 0
//
//0 0 0 0 0 0
//0 3 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 4 0 2 0 3
//0 0 0 4 3 2
//0 0 0 0 0 0
//0 0 0 0 4 4
//0 0 0 0 0 4
//4 0 0 0 0 3
//
//Hint
//
//第一组数据:
//(1,6)爆裂,然后在第二秒(1,4)(2,6)爆裂,在第四秒,两滴水同时到达(3,4), (3,4)变成了6,爆裂,然后在第七秒(3,1)(6,4)变成了2。
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
#include <queue>
using namespace std;
class FlyWater
{
public:
int x;
int y;
int t;//影响时刻
int direc;
};
int place[7][7];
int time[7][7];//爆裂时间
bool CheckInPlace(int x,int y)
{
if(x<=6 && x>=1 && y<=6 && y>=1)
return true;
else
return false;
}
int direction[4][2] = {{0,-1},{0,1},{-1,0},{1,0}};//上下左右
//形参为坐标
void BFS(int x,int y)
{
queue<FlyWater> q;
++place[x][y];
if(place[x][y] > 4)
{
FlyWater fly;
fly.x = x;fly.y = y;fly.t = 0;fly.direc = -1;
time[x][y] = 0;//7:47
q.push(fly);
}
while(!q.empty())
{
FlyWater water = q.front();//pop不返回
q.pop();
int xx = water.x;int yy = water.y;int tt = water.t;int dd = water.direc;
if (dd == -1)
place[xx][yy] = 0;
for(int i =0;i < 4;i++)//上下左右
{
if(dd != -1 && dd != i)
continue;
int a = xx + direction[i][0];int b = yy + direction[i][1];
if(CheckInPlace(a,b))
{
if(place[a][b] == 0)
{
water.x = a;water.y = b;water.t = tt + 1;water.direc = i;
q.push(water);
}
else if (place[a][b] < 4 )
{
++place[a][b];
}
else if(place[a][b] == 4)
{
++place[a][b];//模拟
time[a][b] = tt + 1;//8:02 1.原先错误设置为time[a][b]++;9:02 2.原先是++t
water.x = a;water.y = b;water.t = tt + 1;water.direc = -1;
q.push(water);
}
else
{
//顺着上面 注意:不是顺着上面 根据先前值
if((tt + 1) <= time[a][b])
{
++place[a][b];//不起作用,都会成为0
}
else
{
//此时原先的水珠已经爆破,只是还没有让place[a][b] = 0,所以现在的水珠还将继续飞行,因为一旦为0,终身为0
water.x = a;water.y = b;water.t = tt + 1;water.direc = i;
q.push(water);
}
}
}
}
}
}
int main()
{
while(cin >> place[1][1])
{
//ifstream infile("C:\\Users\\Administrator\\Desktop\\Algorithm\\Problem3\\data3.txt");
for(int i = 1;i<=6;i++)
{
for(int j = 1;j<=6;j++)
{
if(i==1 && j==1)
continue;
cin >> place[i][j];
}
}
int cases;
cin >> cases;
while(cases--)
{
memset(time,-1,sizeof(time));/很重要的初始化
int x;
int y;
cin >> x >>y;
BFS(x,y);
}
for(int i = 1;i<=6;i++)
{
for(int j = 1;j<=6;j++)
{
if(j != 6)
cout << place[i][j]<<" ";
else
cout<<place[i][j];
}
cout<<endl;
}
cout << endl;
}
system("pause");
return 0;
}
过程与心情:
此题前后断断续续用了三天,第一晚上自己睡前想了下,思路在于:...算了,不提也罢,无算法而言,纯属模拟整个游戏,第二天看大牛说BFS算法,遂将算法导论的BFS复习了下,最后动手开写,到不停得遇到BUG,终于搞定,前面的挫败感也一扫而光了,弄得昨晚舜天的比赛也没看,还好,看了估计火气也大,三连败了啊都。
算法:
主要是BFS算法,用自己的话描述:其实是将每一滴即将运动的水滴压入队列,BFS:发现和s距离为k的顶点,然后再搜索距离为k+1的顶点 ,这题是先按时间先影响到的水滴压入队列,出队列的时候再处理,典型的FIFO,运行的时间复杂度是线性的。
编程期间遇到的BUG与易错点:
- 定义一个爆破时间的数组time[7][7],否则不好判断此时经过时,水珠是要被挡住,还是穿过,因为此时可能还没有处理到爆破水滴,根据time[i][j]与即将运行到此处的时间tt+1进行比较
- 爆破时间的初始化 memset(time,-1,sizeof(time))
- 谨慎使用++t,尤其是循环中内部使用到++t ,最后发现的bug就是这个,寻找了很久
- 爆破时间的准确设置很重要