这题虽然是hard难度,但是其实比前两周的简单很多。下面给出题目地址:
https://leetcode.com/problems/cut-off-trees-for-golf-event/description/
本题是一道砍树题。给出一个n*m的矩阵(不大于50*50),矩阵中值为0的数被视为障碍,不能到达或通过。矩阵中值为1的数视为平地,可以通过。大于1的视为树,可以通过。我们的任务就是找一条最短的路径按树的高度从小到大砍掉树,并返回路径长度。其中树被砍掉后就视为草地(其实就是平地)。
本题的想法比较简单,既然要从小到大砍树,那么首先需要把所有树找出来排序(否则找路径时还要全部遍历一遍排大小),这里直接使用set<int>进行排序。然后用bfs计算从前一棵树到后一棵树的最短路径,并将它们累加起来。
下面给上第一次提交的代码:
class Solution {
public:
set<int> trees;
list<int> bfs1;
list<int> bfs2;
list<int> bfs3;
int vis[51][51];
int cutOffTree(vector<vector<int>>& forest) {
int movx[4]={-1,0,1,0};
int movy[4]={0,-1,0,1};
int i,j,lx,ly,step=0,tempx,tempy,temps;
for(i=0;i<forest.size();i++)
{
for(j=0;j<forest[i].size();j++)
{
if(forest[i][j]>1)
trees.insert(forest[i][j]);
}
}
lx=ly=0;
for(set<int>::iterator it=trees.begin();it!=trees.end();it++)
{
memset(vis,0,sizeof(vis));
bfs1.push_back(lx);
bfs2.push_back(ly);
bfs3.push_back(0);
while(!bfs1.empty())
{
tempx=bfs1.front();
tempy=bfs2.front();
temps=bfs3.front();
bfs1.pop_front();
bfs2.pop_front();
bfs3.pop_front();
if(forest[tempy][tempx]==*it)
break;
for(i=0;i<4;i++)
{
if(tempx+movx[i]>=0&&tempx+movx[i]<forest[0].size())
{
if(tempy+movy[i]>=0&&tempy+movy[i]<forest.size())
{
if(!vis[tempy+movy[i]][tempx+movx[i]]&&forest[tempy+movy[i]][tempx+movx[i]]!=0)
{
bfs1.push_back(tempx+movx[i]);
bfs2.push_back(tempy+movy[i]);
bfs3.push_back(temps+1);
vis[tempy+movy[i]][tempx+movx[i]]=1;
}
}
}
}
}
if(forest[tempy][tempx]!=*it)
return -1;
step+=temps;
bfs1.clear();
bfs2.clear();
bfs3.clear();
lx=tempx;
ly=tempy;
}
return step;
}
};
花了960ms,只打败了12%的人,有一些慢。
于是做了一些优化,开始发现每一次用bfs都要用memset重置vis数组,所以保存用bfs的次数j,用vis[x][y]=j来表示节点被第j次bfs访问过了,这就省下了memset的时间。
class Solution {
public:
set<int> trees;
list<int> bfs1;
list<int> bfs2;
list<int> bfs3;
int vis[51][51];
int cutOffTree(vector<vector<int>>& forest) {
int movx[4]={-1,0,1,0};
int movy[4]={0,-1,0,1};
int i,j,lx,ly,step=0,tempx,tempy,temps;
for(i=0;i<forest.size();i++)
{
for(j=0;j<forest[i].size();j++)
{
if(forest[i][j]>1)
trees.insert(forest[i][j]);
}
}
lx=ly=0;
j=1;
for(set<int>::iterator it=trees.begin();it!=trees.end();it++)
{
bfs1.push_back(lx);
bfs2.push_back(ly);
bfs3.push_back(0);
while(!bfs1.empty())
{
tempx=bfs1.front();
tempy=bfs2.front();
temps=bfs3.front();
bfs1.pop_front();
bfs2.pop_front();
bfs3.pop_front();
if(forest[tempy][tempx]==*it)
break;
for(i=0;i<4;i++)
{
if(tempx+movx[i]>=0&&tempx+movx[i]<forest[0].size())
{
if(tempy+movy[i]>=0&&tempy+movy[i]<forest.size())
{
if(vis[tempy+movy[i]][tempx+movx[i]]!=j&&forest[tempy+movy[i]][tempx+movx[i]]!=0)
{
bfs1.push_back(tempx+movx[i]);
bfs2.push_back(tempy+movy[i]);
bfs3.push_back(temps+1);
vis[tempy+movy[i]][tempx+movx[i]]=j;
}
}
}
}
}
if(forest[tempy][tempx]!=*it)
return -1;
step+=temps;
bfs1.clear();
bfs2.clear();
bfs3.clear();
lx=tempx;
ly=tempy;
j++;
}
return step;
}
};
花了588ms,可以看到比上一次提交快了一些,但还是比较慢
然后再想办法优化,一开始认为是set排序速度很慢,于是用数组保存,然后用sort方法排序,结果花了1388ms,慢了好多,于是改了回来 。
然后想可能是用三个队列保存x,y和步长s比较慢,使用一个队列和结构体可能会快一点,然后做了一个结构体。
class Solution {
public:
struct node{
int x,y,step;
};
set<int> trees;
list<node> bfs1;
int vis[51][51];
int cutOffTree(vector<vector<int>>& forest) {
int movx[4]={-1,0,1,0};
int movy[4]={0,-1,0,1};
int i,j,lx,ly,step=0;
node temp;
for(i=0;i<forest.size();i++)
{
for(j=0;j<forest[i].size();j++)
{
if(forest[i][j]>1)
trees.insert(forest[i][j]);
}
}
lx=ly=0;
j=1;
for(set<int>::iterator it=trees.begin();it!=trees.end();it++)
{
node n;
n.x=lx;
n.y=ly;
n.step=0;
bfs1.push_back(n);;
while(!bfs1.empty())
{
temp=bfs1.front();
bfs1.pop_front();
if(forest[temp.y][temp.x]==*it)
break;
for(i=0;i<4;i++)
{
if(temp.x+movx[i]>=0&&temp.x+movx[i]<forest[0].size())
{
if(temp.y+movy[i]>=0&&temp.y+movy[i]<forest.size())
{
if(vis[temp.y+movy[i]][temp.x+movx[i]]!=j&&forest[temp.y+movy[i]][temp.x+movx[i]]!=0)
{
n.x=temp.x+movx[i];
n.y=temp.y+movy[i];
n.step=temp.step+1;
bfs1.push_back(n);
vis[temp.y+movy[i]][temp.x+movx[i]]=j;
}
}
}
}
}
if(forest[temp.y][temp.x]!=*it)
return -1;
step+=temp.step;
bfs1.clear();
lx=temp.x;
ly=temp.y;
j++;
}
return step;
}
};
436ms,看来确实是快了一点,但是还不明显。
最后想到可能queue内部是用数组实现的,比list快。然而我之所以不用queue是因为它没有clear函数,每一次都要重新声明。于是改成了queue试一下。
class Solution {
public:
struct node{
int x,y,step;
};
set<int> trees;
int vis[51][51];
int cutOffTree(vector<vector<int>>& forest) {
int movx[4]={-1,0,1,0};
int movy[4]={0,-1,0,1};
int i,j,lx,ly,step=0;
node temp;
for(i=0;i<forest.size();i++)
{
for(j=0;j<forest[i].size();j++)
{
if(forest[i][j]>1)
trees.insert(forest[i][j]);
}
}
lx=ly=0;
j=1;
for(set<int>::iterator it=trees.begin();it!=trees.end();it++)
{
queue<node> bfs1;
node n;
n.x=lx;
n.y=ly;
n.step=0;
bfs1.push(n);;
while(!bfs1.empty())
{
temp=bfs1.front();
bfs1.pop();
if(forest[temp.y][temp.x]==*it)
break;
for(i=0;i<4;i++)
{
if(temp.x+movx[i]>=0&&temp.x+movx[i]<forest[0].size())
{
if(temp.y+movy[i]>=0&&temp.y+movy[i]<forest.size())
{
if(vis[temp.y+movy[i]][temp.x+movx[i]]!=j&&forest[temp.y+movy[i]][temp.x+movx[i]]!=0)
{
n.x=temp.x+movx[i];
n.y=temp.y+movy[i];
n.step=temp.step+1;
bfs1.push(n);
vis[temp.y+movy[i]][temp.x+movx[i]]=j;
}
}
}
}
}
if(forest[temp.y][temp.x]!=*it)
return -1;
step+=temp.step;
lx=temp.x;
ly=temp.y;
j++;
}
return step;
}
};
还可以,花了248ms,速度终于不错了。
可能将queue用自己写的数组实现还能再快一点,但是queue的速度已经比较不错了。