- 青蛙过河
一只青蛙想要过河。 假定河流被等分为 x 个单元格,并且在每一个单元格内都有可能放有一石子(也有可能没有)。 青蛙可以跳上石头,但是不可以跳入水中。
给定石子的位置列表(用单元格序号升序表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一个石子上)。 开始时, 青蛙默认已站在第一个石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格1跳至单元格2)。
如果青蛙上一步跳跃了 k 个单位,那么它接下来的跳跃距离只能选择为 k - 1、k 或 k + 1个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。
请注意:
石子的数量 ≥ 2 且 < 1100;
每一个石子的位置序号都是一个非负整数,且其 < 231;
第一个石子的位置永远是0。
示例 1:
[0,1,3,5,6,8,12,17]
总共有8个石子。
第一个石子处于序号为0的单元格的位置, 第二个石子处于序号为1的单元格的位置,
第三个石子在序号为3的单元格的位置, 以此定义整个数组…
最后一个石子处于序号为17的单元格的位置。
返回 true。即青蛙可以成功过河,按照如下方案跳跃:
跳1个单位到第2块石子, 然后跳2个单位到第3块石子, 接着
跳2个单位到第4块石子, 然后跳3个单位到第6块石子,
跳4个单位到第7块石子, 最后,跳5个单位到第8个石子(即最后一块石子)。
示例 2:
[0,1,2,3,4,8,9,11]
返回 false。青蛙没有办法过河。
这是因为第5和第6个石子之间的间距太大,没有可选的方案供青蛙跳跃过去。
思路:
此题其实有一点没说明白。就是首次跳跃只能是1
方法一:刚开始的思路我想的是从后往前,也就是说 假如现在我们在 key=ston[i]处
如何判断可以到达key处呢,每次找到比key小的所有的key‘(当然遍历i前面的i=1到i-1即可 为啥是i开始了,因为题目必须保证我们跳过一次才可以进行第二次跳跃) 看能否在key’跳到key处, 然后将所有可以从key’跳到key的所有的跳跃k保存在key.set集合中 此种思想求解dp[key] 我们依据前面的dp[key’]
方法二:
根据现在的dp[key]求解后面的dp[key’]
方法二代码
class Solution {
public:
bool canCross(vector<int>& stones)
{
unordered_map<int,set<int>> dp;//dp代表着到达值为x的石头,所有的k值的集合
// if(stones.size()==2)
// return stones[1]-stones[0]==1?true:false;
if(stones[1]-stones[0]!=1)
{
return false;
}
for(int i=0;i<stones.size();i++)
{
int key=stones[i];
set<int> value;
dp[key]=value;
cout<<"key="<<key<<endl;
}
//由题目可知
//开始时, 青蛙默认已站在第一个石子上,并可以假定它第一步只能跳跃一个单位(即只能从单元格1跳至单元格2)。
dp[stones[1]].insert(stones[1]-stones[0]);
for(int i=1;i<=stones.size()-2;i++)
{
//set<int> s=stones[i];
//dp[i]=s;
int key=stones[i];
for(int k:dp[key] )//dp[key]代表着一个集合 集合中存放着一组k值
{
cout<<"k="<<k<<endl;
for(int nextadd=k-1;nextadd<=k+1;nextadd++) //nextadd=k-1 k k+1
{
// cout<<"*******************"<<endl;
// cout<<"nextadd="<<nextadd<<endl;
// cout<<"key+nextadd="<<key+nextadd<<endl;
// cout<<"*******************"<<endl;
if(nextadd>0&&dp.count(key+nextadd)>0)//key+nextadd也就是下一个元素的key值
{
// cout<<"进来了"<<endl;
// cout<<"key+nextadd="<<key+nextadd<<endl;
dp[key+nextadd].insert(nextadd);
}
}
}
}
int lastKey=stones[stones.size()-1];
return dp[lastKey].size()>0?true:false;
}
};
但是了,我们发现可以用更加统一的方法来做
主要精华点是 dp[0].insert(0);
class Solution {
public:
bool canCross(vector<int>& stones)
{
//注意题目其中有一点我们需要注意 从第一个到第二个点必须只跳长度为1 若是第一个和第二个相差不为1 则是false
unordered_map<int,set<int>> dp;//dp代表着到达值为x的石头,所有的k值的集合
for(int i=0;i<stones.size();i++)
{
int key=stones[i];
set<int> value;
dp[key]=value;
}
dp[0].insert(0);//我觉得应该是 dp[stones[0]]].insert(0);
for(int i=0;i<stones.size();i++)
{
int key=stones[i];
for(int k:dp[key] )//dp[key]代表着一个集合 集合中存放着一组k值
{
for(int nextadd=k-1;nextadd<=k+1;nextadd++) //nextadd=k-1 k k+1
{
if(nextadd>0&&dp.count(key+nextadd)>0)//key+nextadd也就是下一个元素的key值
{
dp[key+nextadd].insert(nextadd);
}
}
}
}
int lastKey=stones[stones.size()-1];
return dp[lastKey].size()>0?true:false;
}
};