https://hjptriplebee.github.io/Google面试经历-一.html/
看到上面这个博客中的一道面试题,如下
有一个n*n的棋盘,上面有m个糖果,最开始有一个人在棋盘左上角,他可以向左向右或者向下移动,但不能向上移动,问他最少需要多少步吃完所有糖果。
写一下自己的思路和代码,因为没有OJ可以验证,不知道对错,暂且记录一下。
思路:动态规划求解
动规计算如果从该位置进入下一行(本行及上面所有行的糖果均已拿到),此时走的最少步数。
我们只要是到一行的进入位置和离开位置,根据糖果的位置拿到这一行糖果的步数就是固定的,所以计算dp[i][j]就是从i-1行的每一个位置当做入口,找到最小的步数即为dp[i][j]的值。
糖果的位置 只需要记录每一行最左和最右糖果的位置即可。
注意:
向下走的那一步要记得加上。
动态规划的空间优化,可以使用两行的数组。
可能存在一整行都没有糖果的情况。
代码使用C++11编译
#include<iostream>
#include<string>
#include<sstream>
#include<fstream>
#include<algorithm>
#include<iterator>
#include<vector>
using namespace std;
vector<vector<int>> input(){
string row;
vector<vector<int>> res;
while(getline(cin,row)){
istringstream ss(row);
istream_iterator<int> eos;
istream_iterator<int> beg(ss);
vector<int> tmp;
copy(beg,eos,back_inserter(tmp));
res.push_back(move(tmp));
}
return res;
}
int min_steps(const vector<vector<int>>& data){
if(data.empty())
return -1;
int row = data.size();
int col = data[0].size();
int est[100000][2];
for(int i=0;i<row;i++)
{
est[i][0]=col;
est[i][1]=-1;
}
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
if(data[i][j]) continue;
if(j<est[i][0]) est[i][0]=j;
if(j>est[i][1]) est[i][1]=j;
}
}
if(row==1)
{
return est[0][1];
}
int m[2][100000];
for(int i=0;i<col;i++)
{
m[0][i]=est[0][1]+abs(i-est[0][1]);
}
int step=0;
for(int i=1;i<row;i++)
{
step=1-step;
for(int j=0;j<col;j++)
{
m[step][j]=m[1-step][j]+1;
if(est[i][1]<est[i][0])
{
est[i][1]=est[i-1][1];
est[i][0]=est[i-1][0];
continue;
}
m[step][j]=1000000;
for(int k=0;k<col;k++)
{
int tmp=est[i][1]-est[i][0];
if(k<est[i][0])
{
tmp+=est[i][0]-k+abs(est[i][1]-j);
}
else if(k>est[i][1])
{
tmp+=k-est[i][1]+abs(est[i][0]-j);
}
else
{
tmp+=min(est[i][1]-k+abs(est[i][0]-j),k-est[i][0]+abs(est[i][1]-j));
}
if(m[1-step][k]+tmp+1<m[step][j]) m[step][j]=m[1-step][k]+tmp+1;
}
}
}
int res=100000;
for(int i=0;i<col;i++)
{
if(m[step][i]<res) res=m[step][i];
}
return res;
}
int main(){
cout << min_steps(input()) << endl;
return 0;
}
20180822更新
经室友提醒,下到下一行的位置一定是本行最左糖果或者最右糖果,所以动态规划的搜索空间变小,可以减少两层循环,同时维护的数组也可以变小,只需要记录从左或者从右下去即可。这样,就也可以写成深度优先搜索,代码只重新写了动态规划的。
#include<iostream>
#include<string>
#include<sstream>
#include<fstream>
#include<algorithm>
#include<iterator>
#include<vector>
using namespace std;
vector<vector<int>> input(){
string row;
vector<vector<int>> res;
while(getline(cin,row)){
istringstream ss(row);
istream_iterator<int> eos;
istream_iterator<int> beg(ss);
vector<int> tmp;
copy(beg,eos,back_inserter(tmp));
res.push_back(move(tmp));
}
return res;
}
int min_steps(const vector<vector<int>>& data){
if(data.empty())
return -1;
int row = data.size();
int col = data[0].size();
int est[100000][2];
for(int i=0;i<row;i++)
{
est[i][0]=col;
est[i][1]=-1;
}
est[0][0]=0;
est[0][1]=0;
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
if(data[i][j]) continue;
if(j<est[i][0]) est[i][0]=j;
if(j>est[i][1]) est[i][1]=j;
}
}
if(row==1)
{
return est[0][1];
}
int m[2][2];
m[0][0]=2*est[0][1]-est[0][0];
m[0][1]=est[0][1];
int step=0;
int a=row-1,b=row-1;
for(int i=1;i<row;i++)
{
step=1-step;
if(est[i][1]<est[i][0])
{
m[step][0]=m[1-step][0]+1;
m[step][1]=m[1-step][1]+1;
est[i][0]=est[i-1][0];
est[i][1]=est[i-1][0];
continue;
}
m[step][0] = est[i][1]-est[i][0]+min(m[1-step][0]+abs(est[i][1]-est[i-1][0]),m[1-step][1]+abs(est[i][1]-est[i-1][1]))+1;
m[step][1] = est[i][1]-est[i][0]+min(m[1-step][0]+abs(est[i][0]-est[i-1][0]),m[1-step][1]+abs(est[i][0]-est[i-1][1]))+1;
a=m[step][0];
b=m[step][1];
}
return min(a,b);
}
int main(){
cout << min_steps(input()) << endl;
return 0;
}