【C++实现】五大常用算法之二(上):BFS算法(实例:接金币)

小赛非常喜欢玩游戏,最近喜欢上了一个接金币的游戏。在游戏中,使用帽子左右移动接金币,金币接的越多越好,但是金币掉到地上就不能再接了。为了方便问题的描述,我们把电脑屏幕分成11格,帽子每次能左右移动一格。现在给电脑屏幕如图标上坐标:


也就是说在游戏里,金币都掉落在0-10这11个位置。开始时帽子刚开始在5这个位置,因此在第一秒,帽子只能接到4,5,6这三个位置中其中一个位置上的金币。问小赛在游戏中最多可能接到多少个金币?(假设帽子可以容纳无穷多个金币)。

题目出处:http://exercise.acmcoder.com/online/online_judge_ques?ques_id=3009&konwledgeId=134

BFS即广度优先搜索,搜索的是图,从当前结点开始,搜索与当前结点距离为k的下一个结点,直到把距离为k的结点搜索完毕,再搜索距离为k+k的所有结点

先上代码:

#include<iostream>
#include<queue>
#include<vector>
#include<string.h>
using namespace std;
#define MAX 100000


struct tmp
{
	int x, y;
};

class BFS
{
public:
	BFS(vector<vector<int>> &dp);
	void bfs(tmp &start,int maxT);//传入开始坐标,最大搜索行数
	int findMaxVal();
private:
	vector<vector<int>> dp;//需要搜索的表
	queue<tmp> que;//BFS算法用到

	int sum[2][11];//保存达到某一步时最大金币数,
	int maxT ;//最大的时间,即需寻找的dp最大行数
	int a[3] ;//左移,不变,右移
	int curT ;//记录当前行索引,如果本行搜索完毕则更新数据

	bool flag[11];//仿真重复计算
};

BFS::BFS(vector<vector<int>> &dp) :curT(0)
{
	this->dp = dp;
	a[0] = -1;
	a[1] =  0;
	a[2] =  1;
	memset(sum, 0, 2 * 11 * sizeof(int));
	memset(flag, false, 11 * sizeof(bool));
}

void BFS::bfs(tmp &start, int maxT)
{
	this->maxT = maxT;
	while (!que.empty())
	{
		que.pop();
	}
	que.push(start);

	while (!que.empty())
	{
		tmp tmp1, tmp2;
		tmp1 = que.front();
		que.pop();
		for (size_t i = 0; i < 3; i++)
		{
			tmp2.x = tmp1.x + 1;
			tmp2.y = tmp1.y + a[i];
			if (tmp2.x>curT)//当前范围结点搜索完毕,更新搜索结果
			{
				curT = tmp2.x;
				for (size_t i = 0; i < 11; i++)
				{
					sum[0][i] = sum[1][i];
					flag[i] = false;
				}
			}
			if (tmp2.x >= 0 && tmp2.x <= maxT && tmp2.y >= 0 && tmp2.y <= 10)//结点有效
			{
				if (flag[tmp2.y] == false)
				{
					que.push(tmp2);
					flag[tmp2.y] = true;
				}				
				int tmp;
				tmp = sum[0][tmp1.y] + dp[tmp2.x][tmp2.y];
				sum[1][tmp2.y] = sum[1][tmp2.y] < tmp ? tmp : sum[1][tmp2.y];//计算结果赋值
			}
		}
	}
}
int BFS::findMaxVal()
{
	int maxCoin=0;
	for (size_t i = 0; i < 11; i++)
	{
		maxCoin = maxCoin < sum[1][i] ? sum[1][i] : maxCoin;
	}
	return maxCoin;
}
int main()
{
	int n, x, T,maxT=0;
	int maxCoin = 0;
	tmp start = { 0, 5 };

	vector<vector<int>> dp;
	queue<int> tmpData;

	/*构建待搜索的表*/
	//如果数据量不大,可直接在栈上分配
	cin >> n;	
	for (size_t i = 0; i < n; i++)
	{
		cin >> x >> T;
		tmpData.push(x);
		tmpData.push(T);
		maxT = maxT < T ? T : maxT;
	}
	
	dp.resize(maxT+1);
	for (size_t i = 0; i <= maxT; i++)
	{
		dp[i].resize(11,0);
	}
	int index_x, index_y;
	for (size_t i = 0; i < n; i++)
	{
		index_y = tmpData.front();
		tmpData.pop();
		index_x = tmpData.front();
		tmpData.pop();
		dp[index_x][index_y]++;
	}
	/*构建待搜索的表*/
	

	//以下真正bfs搜索
	BFS bfs(dp);
	bfs.bfs(start,maxT);

	cout << bfs.findMaxVal() << endl;

	system("pause");
	return 0;
}

核心代码

while (!que.empty())
	{
		tmp tmp1, tmp2;
		tmp1 = que.front();
		que.pop();
		for (size_t i = 0; i < 3; i++)
		{
			tmp2.x = tmp1.x + 1;
			tmp2.y = tmp1.y + a[i];
			if (tmp2.x>curT)//当前范围结点搜索完毕,更新搜索结果
			{
				curT = tmp2.x;
				for (size_t i = 0; i < 11; i++)
				{
					sum[0][i] = sum[1][i];
					flag[i] = false;
				}
			}
			if (tmp2.x >= 0 && tmp2.x <= maxT && tmp2.y >= 0 && tmp2.y <= 10)//结点有效
			{
				if (flag[tmp2.y] == false)
				{
					que.push(tmp2);
					flag[tmp2.y] = true;
				}				
				int tmp;
				tmp = sum[0][tmp1.y] + dp[tmp2.x][tmp2.y];
				sum[1][tmp2.y] = sum[1][tmp2.y] < tmp ? tmp : sum[1][tmp2.y];//计算结果赋值
			}
		}
	}

解题思路:

把时间看作行数,位置看作列数,为了使dp动态分配,因此main函数中给dp赋值这段代码占了很大篇幅

把当前结点(x,y),第一次搜索范围(x+1,y-1),(x+1,y),(x+1,y+1);即最多只能左右移动一格,然后计算到达每一个位置所获得的最大金币数sum[1][tmp2.y] = sum[1][tmp2.y] < tmp ? tmp : sum[1][tmp2.y];

之所以sum定义成二维数组,是因为(如下图所示)第一种情况计算得到1,第二种情况计算得到2,而此题是获取最大金币数,所以增加了判断

if (flag[tmp2.y] == false)
{
  que.push(tmp2);
  flag[tmp2.y] = true;
}	

此处是为了防止重复计算,如果某个结点已被push入队列,则不在push

猜你喜欢

转载自blog.csdn.net/lnplnp_/article/details/81146520