小赛非常喜欢玩游戏,最近喜欢上了一个接金币的游戏。在游戏中,使用帽子左右移动接金币,金币接的越多越好,但是金币掉到地上就不能再接了。为了方便问题的描述,我们把电脑屏幕分成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