仙岛求药(二)
【试题描述】
咳,上回说到李逍遥去求药,其实他找到药之后,还需要给他的婶婶送过去,所以他需要在最短的时间内找到要并且到达婶婶(‘s‘)所在的位置。
【输入要求】
输入有多组测试数据. 每组测试数据以两个非零整数 M 和 N 开始,两者均不大于100。M 表示迷阵行数, N 表示迷阵列数。接下来有 M 行, 每行包含N个字符,不同字符分别代表不同含义:
1) ‘@’:少年李逍遥所在的位置;
2) ‘.’:可以安全通行的方格;
3) ‘#’:有怪物的方格;
4) ‘*’:仙药所在位置。
5) ‘s‘ : 婶婶所在的位置。
当在一行中读入的是两个零时,表示输入结束。
【输出要求】
对于每组测试数据,分别输出一行,该行包含李逍遥找到仙药并且送给婶婶需要穿过的最少的方格数目(计数包括初始位置的方块)。如果他不可能找到仙药, 则输出 -1。
【输入实例】
8 8 .@##...# #....#.# #.#.##.. *.#.###. #.#...#. ..###*#. ...#.s.. .#.*.### 0 0
【输出实例】
10
【其他说明】
无
【试题分析】
仔细一看,可以瞬间想到:遍历到每个仙药再回到婶婶,求最小值,那么只需要这么写:
#include<iostream>
#include<cstring>
using namespace std;
char map[101][101];
int a[101][101],ans=0,m,n;
void res(int u,int v,int i,int j)
{
int t=a[u][v];
if(u==i&&v==j) ans=t;
t++;
if(v<m-1&&map[u][v+1]!=‘#‘&&a[u][v+1]>t)
{
a[u][v+1]=t;
res(u,v+1,i,j);
}
if(u>0&&map[u-1][v]!=‘#‘&&a[u-1][v]>t)
{
a[u-1][v]=t;
res(u-1,v,i,j);
}
if(v>0&&map[u][v-1]!=‘#‘&&a[u][v-1]>t)
{
a[u][v-1]=t;
res(u,v-1,i,j);
}
if(u<n-1&&map[u+1][v]!=‘#‘&&a[u+1][v]>t)
{
a[u+1][v]=t;
res(u+1,v,i,j);
}
}
int startx,starty,endx[10000],endy[10000],k=0,ans1=9999999,endx2,endy2,k2=0,ans2=9999999,sx,sy,ans3=9999999;
int main()
{
while(scanf("%d%d",&n,&m)&&m!=0&&n!=0)
{
memset(a,1,sizeof(a));
memset(endx,0,sizeof(endx));
memset(endy,0,sizeof(endy));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
scanf("%c",&map[i][j]);
if(map[i][j]==‘@‘)
{
startx=i;
starty=j;
}
if(map[i][j]==‘*‘)
{
endx[k]=i;
endy[k++]=j;
}
if(map[i][j]==‘s‘)
{
endx2=i;
endy2=j;
}
}
getchar();
}
memset(a,1,sizeof(a));
a[startx][starty]=0;
for(int i=0;i<k;i++)//枚举
{
memset(a,1,sizeof(a));//分段考虑
a[startx][starty]=0;
res(startx,starty,endx[i],endy[i]);
ans1=ans,sx=endx[i],sy=endy[i];//赋值
ans=0;
memset(a,1,sizeof(a));
a[sx][sy]=0;
res(sx,sy,endx2,endy2);
ans2=ans;
if(ans3>ans2+ans1) ans3=ans1+ans2//更新;
ans=0;
}
if(ans3<9999999)cout<<ans3<<endl;
else cout<<-1;
ans3=9999999;//更新
ans1=9999999;
ans2=9999999;
k2=0;
k=0;
}
}
好了,写完代码了。
如果你认为对了,那么可以输入这组数据试一试
20 50 @************************************************* ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** ************************************************** *************************************************s
按这样来说,每一个*都要被算一次,这样的复杂度即使是喜欢打暴力的人也是不能容忍的,当然,改进算法才是我们的重点,但是怎样优化呢?
用BFS???
不好吧,那也会超限,但是正解+BFS是很快的。
用DFS???
更差了……
我们先来想一想这样一个问题,如果一个人要从上海开车去北京旅游,他会把所有路都傻傻的走一遍吗?当然不会了,因为人类有一张王牌——地图,我们可以去估一估最短距离。
那么这道题也是一样的,我们可以大概估一个两点之间最短距离值,算出它以后,因为我们默认为两点之间没有障碍,所以它就是最短路了,如果这样算的话还是大于等于原来我们记录的那个最小值,那实际的路径一定比这个估算最优值大或等于,由此,我们可以去除多个药的枚举,就像人人皆知的KMP算法一样,这种算法名叫启发式搜索,是人工智能的一个知识点,不一定要将它完全地写出代码,但至少要了解并会使用。
我们的代码只需要加上这么一行,就能大幅度提高效率(往往算法的精髓就在于此):
if(abs(endx[i]+endy[i]-startx-starty)+abs(endx2+endy2-endx[i]-endy[i])<ans3)
【代码】
#include<iostream>
#include<cstring>
using namespace std;
char map[101][101];
int a[101][101],ans=0,m,n;
void res(int u,int v,int i,int j)
{
int t=a[u][v];
if(u==i&&v==j) ans=t;
t++;
if(v<m-1&&map[u][v+1]!=‘#‘&&a[u][v+1]>t)
{
a[u][v+1]=t;
res(u,v+1,i,j);
}
if(u>0&&map[u-1][v]!=‘#‘&&a[u-1][v]>t)
{
a[u-1][v]=t;
res(u-1,v,i,j);
}
if(v>0&&map[u][v-1]!=‘#‘&&a[u][v-1]>t)
{
a[u][v-1]=t;
res(u,v-1,i,j);
}
if(u<n-1&&map[u+1][v]!=‘#‘&&a[u+1][v]>t)
{
a[u+1][v]=t;
res(u+1,v,i,j);
}
}
int startx,starty,endx[10000],endy[10000],k=0,ans1=9999999,endx2,endy2,k2=0,ans2=9999999,sx,sy,ans3=9999999;
int main()
{
while(cin>>n>>m&&m!=0&&n!=0)
{
memset(a,1,sizeof(a));
memset(endx,0,sizeof(endx));
memset(endy,0,sizeof(endy));
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>map[i][j];
if(map[i][j]==‘@‘)
{
startx=i;
starty=j;
}
if(map[i][j]==‘*‘)
{
endx[k]=i;
endy[k++]=j;
}
if(map[i][j]==‘s‘)
{
endx2=i;
endy2=j;
}
}
}
memset(a,1,sizeof(a));
a[startx][starty]=0;
for(int i=0;i<k;i++)
{
if(abs(endx[i]+endy[i]-startx-starty)+abs(endx2+endy2-endx[i]-endy[i])<ans3)
//判定,如果此代码其它地方不理解可以去看仙岛求药(一)
{
memset(a,1,sizeof(a));
a[startx][starty]=0;
res(startx,starty,endx[i],endy[i]);
ans1=ans,sx=endx[i],sy=endy[i];
ans=0;
memset(a,1,sizeof(a));
a[sx][sy]=0;
res(sx,sy,endx2,endy2);
ans2=ans;
if(ans3>ans2+ans1) ans3=ans1+ans2;
ans=0;
}
}
cout<<ans3<<endl;
ans3=9999999;
ans1=9999999;
ans2=9999999;
k2=0;
k=0;
}
}
原文:http://www.cnblogs.com/wxjor/p/5697428.html