google面试题

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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值