2014WAP校园招聘笔试题

14 篇文章 0 订阅

这是2014年WAP笔试的第一题,当时没有做出了,最近在网上看到一篇博客写了关于这道题的解法,自己看了看用C++实现了。博客地址:http://blog.csdn.net/litoupu/article/details/41220817

题目:

Abstract
We are planning an orienteering game.
The aim of this game is to arrive at the goal (G) from the start (S) with the shortest distance.
However, the players have to pass all the checkpoints (@) on the map.
An orienteering map is to be given in the following format.
########
#@....G#
##.##@##
#..@..S#
#@.....#
########
In this problem, an orienteering map is to be given.
Calculate the minimum distance from the start to the goal with passing all the checkpoints.
Specification
* A map consists of 5 characters as following.
You can assume that the map does not contain any invalid characters and
the map has exactly one start symbol 'S' and exactly one goal symbol 'G'.
* 'S' means the orienteering start.
* 'G' means the orienteering goal.
* '@' means an orienteering checkpoint.
* '.' means an opened-block that players can pass.
* '#' means a closed-block that players cannot pass.
* It is allowed to move only by one step vertically or horizontally (up, down, left, or right) to the
next block.
Other types of movements, such as moving diagonally (left up, right up, left down and right down)
and skipping one or more blocks, are NOT permitted.
* You MUST NOT get out of the map.
* Distance is to be defined as the number of movements to the different blocks.
* You CAN pass opened-blocks, checkpoints, the start, and the goal more than once if necessary.
* You can assume that parameters satisfy following conditions.
* 1 <= width <= 100
* 1 <= height <= 100
* The maximum number of checkpoints is 18.
* Return -1 if given arguments do not satisfy specifications, or players cannot arrive at the goal

from the start by passing all the checkpoints.



解题思路:

本题是一道动态规划问题,问题的难点在于如何存储中间子问题的结果,比如这道题是求从起点S经过所有的@到达终点G求其最短路径,以题目中的例子为例:S编号为0, G编号为5,其他@节点从上到下从左到右依次编号为1,2,3,4,我们求到达G的最短路径我们可以分解为是从1到达还是从2,3,4到达G这样就转换成求解到达1,2,3,4四个较小的子问题,依次类推把问题不断缩小。这里有一个问题就是到达某个checkpoints  i 不是仅仅的求其从起点S到达此i的最短路径而是要经过除其后路径上的@的节点所有@节点,比如我们是从4到达G的但是从S到达4有好多种方法如S-1-2-3,S-1-3-2,S-2-1-3...等等他们又可以分为许多更小的子问题,为了不重复计算需要把这些中间结果都存储下来,这么在以后的计算中快速判断和找到这个子问题是否已经计算过了呢?参考的博客中使用 位 很好的解决了这个问题,比如本例中有四个checkpoints那么我们就用四位二进制来表示:XXXX,(X取0或1)分别代表第四个第二个第三个第一个checkpoint,0101表示到达指定的节点需要经过第三个和第一个节点,1001表示到达指定节点需要经过第一个和第四个节点。以下代码中slove(G,1111)表示从起点S经过四个chekpoints到达G的最短路径。

代码:


#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cmath>

using namespace std;

const int MAX_VAL = 1000000000;//不可达 
//到达目的的路径个数的上限值 ,这个值取多大取决于从S经过所有的@到达G有多少条不同的路径(全排列)
const int LEN = 524288; 
const int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};//搜索的四个方向 

struct node
{
	int r;
	int c;
	int step;
};

char ori_map[102][102];//地图 
unsigned int index[102][102];//给起点S,终点G以及每一个checkpoints节点分配一个索引0,1,2.... 
bool isVisited[102][102];//记录是否走过这个节点 
int width, height;
int checkpoints = 0;//记录checkpoints个数 
int S_R, S_C, G_R, G_C;//起点和终点的行列坐标 

int minDistance[20][20];//任意S,G或@节点之间的最小距离
//state[i][j]中j转化为二进制表示每一位代表一个checkpoint节点,
//若是1就代表到达i节点必须要走这个节点 ,比如j = 0101b就表示到达i要经过第一个和第三个checkpoint 
int state[20][LEN];

void bfs(node start)
{
	memset(isVisited, false, sizeof(isVisited));
	queue<node> q;
	q.push(start);
	
	while(!q.empty())
	{
		node first = q.front();
		q.pop();
		int r = first.r;
		int c = first.c;
		int step = first.step;
		isVisited[r][c] = true;
		for(int i=0;i<4; ++i)
		{
			int nextr = r + dir[i][0];
			int nextc = c + dir[i][1];
			if(nextr>=0&&nextr<height
			   &&nextc>=0&&nextc<width
			   &&isVisited[nextr][nextc]==false
			   &&ori_map[nextr][nextc]!='#')
			{
				isVisited[nextr][nextc] = true;
				node tmp;
				tmp.r = nextr;
				tmp.c = nextc;
				tmp.step = step + 1;
				q.push(tmp);
				//if('S'==ori_map[nextr][nextc]||'G'==ori_map[nextr][nextc]||'@'==ori_map[nextr][nextc])
				if('.'!=ori_map[nextr][nextc])
				{
					minDistance[index[start.r][start.c]][index[nextr][nextc]] = step + 1;
				}
			}
		}
	}
}

void getminDistance()
{
	for(int i=0; i<height; ++i)
	{
		for(int j=0; j<width; ++j)
		{
			if('S'==ori_map[i][j] || 'G'==ori_map[i][j] || '@'==ori_map[i][j])
			{
				node tmp;
				tmp.r = i;
				tmp.c = j;
				tmp.step = 0;
				bfs(tmp);
			}
		}
	}
}

int slove(int k, int s)
{
	if(state[k][s]>0&&state[k][s]<MAX_VAL)
	{
		return state[k][s];
	}
	int ans = MAX_VAL;
	for(int i=1; i<checkpoints+2; ++i)
	{
		if((s & (1<<(i-1)))!=0)
		{
			int subSlove = slove(i, s^(1<<(i-1)));
			if(subSlove!=MAX_VAL && minDistance[i][k]!=MAX_VAL)
			{
				int subAns = subSlove + minDistance[i][k];
				if(subAns<ans)
				{
					ans = subAns;
				}
			}
		}
	}
	
	state[k][s] = ans;
	return ans;
}

int main()
{
	cin>>width>>height;
	for(int i=0;i<height;i++)
	{
		for(int j=0;j<width;++j)
		{
			cin>>ori_map[i][j];
			switch(ori_map[i][j])
			{
				case 'S':
					S_R = i;
					S_C = j;
					index[i][j] = 0;
					break;
				case 'G':
					G_R = i;
					G_C = j;
					break;
				case '@':
					++checkpoints;
					index[i][j] = checkpoints;
					break;
			}
		}
	}
	
	index[G_R][G_C] = checkpoints+1;
	
	for(int i=0; i<checkpoints+1; ++i)
	{
		minDistance[i][i] = 0;
		for(int j=i+1; j<checkpoints+2; ++j)
		{
			minDistance[i][j] = MAX_VAL;
			minDistance[j][i] = MAX_VAL;
		}
	}
	
	getminDistance();
	
	for(int i=1; i<checkpoints+2; ++i)
	{
		state[i][0] = minDistance[i][0];
	}
	
	int end_state = (1<<checkpoints) - 1;;
	int len = slove(checkpoints+1, end_state);
	
	if(MAX_VAL == len)
	{
		cout << -1 << endl;
	}
	else
	{
		cout << len << endl;
	}
	
	return 0;
}

为题目中的例子附加张图来帮助理解代码中slove的递归操作吧:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值