算法专题训练 搜索a-T3 Ni骑士(ni)
搞了半天八数码弄不出来就只好来打题解 这道题是在搜索a碰到的(链接: http://pan.baidu.com/s/1jG9rQsQ ) 感觉题目最大亮点就是这英文简写"ni", 真是令人感慨出题老师的才华啊 好了,废话不多说 题目内容如下(闲烦请无视之,下下方有简单的介绍):
【问题描述】
贝 西(Bessie)在卡摩洛遇到了棘手的情况:她必须穿越由Ni骑士把守的森林。 骑士答应Bessie
只要 Bessie 能给他们带来一丛灌木就能安全穿越森林。时间宝贵,Bessie 必须尽快找到一丛灌木
然后给骑士送去。
Bessie有一张森林的地图。森林被划分成了W×H个网格(1≤W≤1000; 1≤H≤1000)。 每 个
网格用0~4的整数表示。
0:可以通行;
1:无法通行(沼泽、悬崖、杀手) ;
2:Bessie的起始位置;
3:骑士的起始位置;
4:灌木丛。
Bessie 只能朝上下左右四个方向行走,每走一个网格要花一天的时间,请超出一条到达骑士的
最短路。问题保证有解。
【输入】
输入文件的第一行是W和H;
一般来讲,后面有 H 行每行 W 个整数(即 0~4),表示地图。但是当某行的整数个数超过 40 个
时,就会另起一行。
【输出】
输出Bessie至少需要几天才能把灌木丛带给骑士。
【输入样例】
8 4
4 1 0 0 0 0 1 0
0 0 0 1 0 1 0 0
0 2 1 1 3 0 4 0
0 0 0 4 1 1 1 0
【输出样例】
11
【Hint】
Explanation of the sample:
Width=8, height=4. Bessie starts on the third row, only a few squares away from the Knights.
Bessie can move in this pattern to get a shrubbery for the Knights: N, W, N, S, E, E, N, E, E, S, S. She gets the
shrubbery in the northwest corner and then makes her away around the barriers to the east and then south to
the Knights.
题目内容一大堆,说简单点就是给出一张W*H(1<=W,H<=1000)的地图,每个位置都标有0..4的数字。其中0表示可以通过,1表示不能通过,2表示Bessie的起始位置,3表示骑士起始位置,4表示树丛。现在要求求出一条最短的路,使骑士经过树丛至少一次,且能够到达Bessie所在位置。
一开始看到题目认为这个好简单啊 不就是一个裸的bfs吗 于是就开始打了第一遍代码 打了之后发现思路很混乱 往哪儿开始搜索都搞不清 于是再看题目 这一看就明白了 就是题中的单精度小女孩(......)要去找它的骑士大哥(666......)当然,这位小女孩还要经过一个树丛柑橘(这不是废话吗)
这给人第一感觉就是从树丛开始往girl和boy搜素 用一个s数组保存到某个点的最小距离 一开始值设置成0 然后从第一个树开始一个个搜过去 每搜一次保存最小值 也就是s[a1][b1]+s[a2][b2](a1,b1,a2,b2分别是girl和boy的坐标位置 当然这里也需要注意一下如果没有搜到的话 bfs也是会退出的 这样的话相应s数组的值也可能还是0 这里就要加一个特判 也就是当两个s的值都不等于0时才进行 (一开始答案输出老是0 看到这里把数据输出来才知道被坑了 所以说数组返回值有风险(适当的宾语前置。),使用需谨慎。
好像又讲开了,那么我们还是回归正题。(啊,正题是什么......) 好,这样几遍的bfs之后样例就能正确输出了 (好高兴啊) 过程代码如下:
void bfs(int a,int b)
{
s[a][b]=;
q[].push(a);
q[].push(b);
q[].push();
while((!q[].empty())&&(!q[].empty())&&(!q[].empty()))
{
for(int d=;d<=;d++)
{
int x=q[].front()+dx[d];
int y=q[].front()+dy[d];
if(g[x][y]==||x<||x>h||y<||y>w)
continue;
if(s[x][y]==)
{
s[x][y]=q[].front()+;
// cout<<"x "<<x<<' '<<"y "<<y<<endl;
// cout<<"s "<<s[x][y]<<endl;
sum2++;
q[].push(x);
q[].push(y);
q[].push(s[x][y]);
if(s[a1][b1]>&&s[a2][b2]>)
{
// cout<<s[a1][b1]<<' '<<s[a2][b2]<<endl;
return;
}
}
}
q[].pop();
q[].pop();
q[].pop();
}
}
然后去测 只过了8个点 (很郁闷 这难道不是bfs吗 再看看数据100w 应该能过嘛 )但再看 我这里的不止是一次bfs 而是对于每个草丛都要来一次bfs 复杂度就由此极大的提高了 有没有办法剪枝呢?
于是我会教室又去想 终于在眼保健操的时候有了一点灵感: 既然问题的一方面出在对于每个树都搜过去而造成了某种意义上的超时 那么我们是不是可以在这里加个条件以减少bfs的次数呢? 如果当前搜的书与起点终点的曼哈顿距离已经比minn大了 那么
无论怎么走都不可能了 和以前做的一道售货员的难题的剪枝像 然后,看到了教室前面开着的电脑 内心想着:反正才第一节嘛 还有时间 于是我就飞奔地上去测了 果然原来6s跑出来的第九个点 这次只要1.3s了 呵呵,然后记得那节晚读整个人都好了
可是 ,1.3s只是较以前的6s有了一些较大的改进 但还远远不够
那么,再接着想:既然我在bfs的做之前加了这么一个条件 那么 在bfs内部拓展节点的时候是不是也可以试着加个类似的条件呢? 仔细想想bfs的“原理” 它是从近往远的每一排节点进行拓展的 也就是说 下一次取队头出来的肯定是要比这次大的 好 那么在有了理论依据和现实基础后 我果断的在进行是否拓展过的判断之前加了这样一个条件
问题似乎迎刃而解了 再去测 第九个点终于闯入了1s大关 0.7! 再怀着兴奋的心情去搞第十个点时 答案却也再没有显示出来了 (好忧伤......)
于是我又再想 假如他数据前面几次的minn是很大但又递减的呢 这样的话我在bfs前的判断不久跟没做过一样嘛 WTF! 但转念一想 数据是死的但人是活的嘛 于是我就想干脆对每个树枝对于其道起点终点的曼哈顿距离进行排序 如下图所示:
然后终于 第十个点在教室0.7s跑出来了!!!!! (记得我后来高兴了一节历史课) 然后去机房测 0.3 好了,这道题就在机房终于a掉了
顺便发表一下我的感慨:真的感觉最后把这题做出来很有成就感 每一次改进都是一次小的跃进 oi或许正是需要这种不懈的探索精神吧
难道故事就这样结束了吗?(Q:有的人认为这句话也可以去掉但也有人说不可以去掉 你认为呢? A:这句话承上启下 起到了过渡的作用 同时亦吸引了读者的阅读兴趣 (666...))
好讲到这里我便先把前面的ac代码放出来给大家看看 (由于配置不够 在家测要1s+ 请勿见怪 本文最后亦会提及一种更好的方法)
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=+;
struct node
{
int x,y;
}data[maxn];
int g[maxn][maxn],s[maxn][maxn];
int dx[]={,,,-,};
int dy[]={,,,,-};
int w,h,cnt=,sum1=,sum2=;
int a1,b1,a2,b2,k1;
int s1,s2,minn=0x7f7f7f;
queue<int>q[];
int distance1(int a,int b)
{
return (abs(a1-a)+abs(a2-a)+abs(b1-b)+abs(b2-b));
}
void bfs(int a,int b)
{
sum1++;
// queue<int>q[4];
s[a][b]=;
q[].push(a);
q[].push(b);
q[].push();
while((!q[].empty())&&(!q[].empty())&&(!q[].empty()))
{
for(int d=;d<=;d++)
{
int x=q[].front()+dx[d];
int y=q[].front()+dy[d];
if(g[x][y]==||x<||x>h||y<||y>w)
continue;
if(distance1(x,y)>minn)
continue;
if(s[x][y]==)
{
s[x][y]=q[].front()+;
// cout<<"x "<<x<<' '<<"y "<<y<<endl;
// cout<<"s "<<s[x][y]<<endl;
sum2++;
q[].push(x);
q[].push(y);
q[].push(s[x][y]);
if(s[a1][b1]>&&s[a2][b2]>)
{
// cout<<s[a1][b1]<<' '<<s[a2][b2]<<endl;
return;
}
}
}
q[].pop();
q[].pop();
q[].pop();
}
}
bool my_comp(node a,node b)
{
return (distance1(a.x,a.y)<distance1(b.x,b.y));
}
int main()
{
ios::sync_with_stdio(false);
// freopen("ni.in","r",stdin);
// freopen("ni.out","w",stdout);
freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
cin>>w>>h;
for(int i=;i<=h;i++)
for(int j=;j<=w;j++)
{
cin>>g[i][j];
if(g[i][j]==)
{
a1=i;
b1=j;
}
if(g[i][j]==)
{
a2=i;
b2=j;
}
if(g[i][j]==)
{
k1++;
data[k1].x=i;
data[k1].y=j;
}
}
// for(int i=1;i<=h;i++)
// {
// for(int j=1;j<=w;j++)
// cout<<g[i][j]<<' ';
// cout<<endl;
// }
// cout<<g[2][1]<<endl;
// cout<<g[2][1]<<endl;
// for(int i=1;i<=h;i++)
// for(int j=1;j<=w;j++)
// if(g[i][j]==4)
sort(data+,data+k1+,my_comp);
for(int i=;i<=k1;i++)
{
if(distance1(data[i].x,data[i].y)>minn)
continue;
while((!q[].empty())&&(!q[].empty())&&(!q[].empty()))
{
if(!q[].empty())
q[].pop();
if(!q[].empty())
q[].pop();
if(!q[].empty())
q[].pop();
}
// cout<<i<<' '<<j<<endl;
// while(!q[1].empty())
// q[1].pop();
// while(!q[2].empty())
// q[2].pop();
// while(!q[3].empty())
// q[3].pop(); memset(s,,sizeof(s));
bfs(data[i].x,data[i].y);
// cout<<s[a1][b1]<<' '<<s[a2][b2]<<endl;
if(s[a1][b1]!=&&s[a2][b2]!=&&s[a1][b1]+s[a2][b2]<minn)
minn=s[a1][b1]+s[a2][b2];
}
// cout<<sum1<<' '<<sum2<<endl;
cout<<minn<<endl;
// cout << "\n" << (double)clock() / CLOCKS_PER_SEC;
// cout<<sum<<endl;
return ;
}
好了 ,起床 ,开更
对于上面说的方法我刚刚又再敲了一遍 从girl和boy出发 全图遍历至所有的树丛都找到为止 在这里需要一个判断 由于才疏学浅我就直接线性遍历数组了
对了 还有一点就是代码中的bfs过程中可以直接引用数组的参数 但我弄了半天都不行(*s ? s[ ]) 于是我就直接重新copy了一遍过程(弱弱的气息......)
但实际上可以用hash 康托展开等方法来实现之 这些的话等我以后有时间再把代码补上吧 家里电脑(配置很烂,当年机房0.2s的家里跑了3s)勉强1s能过 如右图所示:
代码如下:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=+;
struct node
{
int x,y;
}data[maxn];
int g[maxn][maxn],s1[][],s2[][];
int dx[]={,,,-,};
int dy[]={,,,,-};
int w,h,cnt=,sum1=,sum2=;
int a1,b1,a2,b2,k1,k2;
int minn=0x7f7f7f;
queue<int>q[];
int distance1(int a,int b)
{
return (abs(a1-a)+abs(a2-a)+abs(b1-b)+abs(b2-b));
}
bool all1()
{
for(int i=;i<=k1;i++)
if(s1[data[i].x][data[i].y]==)
return false;
return true;
}
bool all2()
{
for(int i=;i<=k1;i++)
if(s2[data[i].x][data[i].y]==)
return false;
return true;
}
void bfs1(int a,int b)
{
// sum1++;
// queue<int>q[4];
s1[a][b]=;
q[].push(a);
q[].push(b);
q[].push();
while((!q[].empty())&&(!q[].empty())&&(!q[].empty()))
{
for(int d=;d<=;d++)
{
int x=q[].front()+dx[d];
int y=q[].front()+dy[d];
if(g[x][y]==||x<||x>h||y<||y>w)
continue;
// if(distance1(x,y)>minn)
// continue;
if(s1[x][y]==)
{
s1[x][y]=q[].front()+;
// cout<<"x "<<x<<' '<<"y "<<y<<endl;
// cout<<"s "<<s[x][y]<<endl;
// sum2++;
q[].push(x);
q[].push(y);
q[].push(s1[x][y]);
if(all1())
{
// cout<<s[a1][b1]<<' '<<s[a2][b2]<<endl;
return;
}
}
}
q[].pop();
q[].pop();
q[].pop();
}
}
void bfs2(int a,int b)
{
// sum1++;
// queue<int>q[4];
s2[a][b]=;
q[].push(a);
q[].push(b);
q[].push();
while((!q[].empty())&&(!q[].empty())&&(!q[].empty()))
{
for(int d=;d<=;d++)
{
int x=q[].front()+dx[d];
int y=q[].front()+dy[d];
if(g[x][y]==||x<||x>h||y<||y>w)
continue;
// if(distance1(x,y)>minn)
// continue;
if(s2[x][y]==)
{
s2[x][y]=q[].front()+;
// cout<<"x "<<x<<' '<<"y "<<y<<endl;
// cout<<"s "<<s[x][y]<<endl;
// sum2++;
q[].push(x);
q[].push(y);
q[].push(s2[x][y]);
if(all2())
{
// cout<<s[a1][b1]<<' '<<s[a2][b2]<<endl;
return;
}
}
}
q[].pop();
q[].pop();
q[].pop();
}
}
bool my_comp(node a,node b)
{
return (distance1(a.x,a.y)<distance1(b.x,b.y));
}
int main()
{
ios::sync_with_stdio(false);
freopen("ni.in","r",stdin);
freopen("ni.out","w",stdout);
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
cin>>w>>h;
for(int i=;i<=h;i++)
for(int j=;j<=w;j++)
{
cin>>g[i][j];
if(g[i][j]==)
{
a1=i;
b1=j;
}
if(g[i][j]==)
{
a2=i;
b2=j;
}
if(g[i][j]==)
{
k1++;
data[k1].x=i;
data[k1].y=j;
}
}
bfs1(a1,b1);
while((!q[].empty())&&(!q[].empty())&&(!q[].empty()))
{
if(!q[].empty())
q[].pop();
if(!q[].empty())
q[].pop();
if(!q[].empty())
q[].pop();
}
bfs2(a2,b2);
for(int i=;i<=k1;i++)
{
if(s1[data[i].x][data[i].y]!=&&s2[data[i].x][data[i].y]!=)
minn=min(minn,s1[data[i].x][data[i].y]+s2[data[i].x][data[i].y]);
}
cout<<minn<<endl;
// for(int i=1;i<=h;i++)
// {
// for(int j=1;j<=w;j++)
// cout<<g[i][j]<<' ';
// cout<<endl;
// }
// cout<<g[2][1]<<endl;
// cout<<g[2][1]<<endl;
// for(int i=1;i<=h;i++)
// for(int j=1;j<=w;j++)
// if(g[i][j]==4)
// sort(data+1,data+k1+1,my_comp);
// for(int i=1;i<=k1;i++)
// {
// if(distance1(data[i].x,data[i].y)>minn)
// continue;
// while((!q[1].empty())&&(!q[2].empty())&&(!q[3].empty()))
// {
// if(!q[1].empty())
// q[1].pop();
// if(!q[2].empty())
// q[2].pop();
// if(!q[3].empty())
// q[3].pop();
// }
//// cout<<i<<' '<<j<<endl;
//// while(!q[1].empty())
//// q[1].pop();
//// while(!q[2].empty())
//// q[2].pop();
//// while(!q[3].empty())
//// q[3].pop();
//
// memset(s,0,sizeof(s));
// bfs(data[i].x,data[i].y);
//// cout<<s[a1][b1]<<' '<<s[a2][b2]<<endl;
// if(s[a1][b1]!=0&&s[a2][b2]!=0&&s[a1][b1]+s[a2][b2]<minn)
// minn=s[a1][b1]+s[a2][b2];
// }
// // cout<<sum1<<' '<<sum2<<endl;
// cout<<minn<<endl;
// cout << "\n" << (double)clock() / CLOCKS_PER_SEC;
// cout<<sum<<endl;
return ;
}
算法专题训练 搜索a-T3 Ni骑士(ni)的更多相关文章
- Leedcode算法专题训练(搜索)
BFS 广度优先搜索一层一层地进行遍历,每层遍历都是以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点.需要注意的是,遍历过的节点不能再次被遍历. 第一层: 0 -> {6,2,1,5} ...
- [算法专题] 深度优先搜索&回溯剪枝
1. Palindrome Partitioning https://leetcode.com/problems/palindrome-partitioning/ Given a string s, ...
- Leedcode算法专题训练(树)
递归 一棵树要么是空树,要么有两个指针,每个指针指向一棵树.树是一种递归结构,很多树的问题可以使用递归来处理. 1. 树的高度 104. Maximum Depth of Binary Tree (E ...
- Leedcode算法专题训练(动态规划)
递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解,避免重复计算. 斐波那契数列 1. 爬楼梯 70. Climbing Stairs (Easy) L ...
- Leedcode算法专题训练(分治法)
归并排序就是一个用分治法的经典例子,这里我用它来举例描述一下上面的步骤: 1.归并排序首先把原问题拆分成2个规模更小的子问题. 2.递归地求解子问题,当子问题规模足够小时,可以一下子解决它.在这个例子 ...
- Leedcode算法专题训练(二分查找)
二分查找实现 非常详细的解释,简单但是细节很重要 https://www.cnblogs.com/kyoner/p/11080078.html 正常实现 Input : [1,2,3,4,5] key ...
- Leedcode算法专题训练(排序)
排序 快速排序 用于求解 Kth Element 问题,也就是第 K 个元素的问题. 可以使用快速排序的 partition() 进行实现.需要先打乱数组,否则最坏情况下时间复杂度为 O(N2). 堆 ...
- Leedcode算法专题训练(贪心)
1. 分配饼干 455. 分发饼干 题目描述:每个孩子都有一个满足度 grid,每个饼干都有一个大小 size,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足.求解最多可以获得满足的孩子数 ...
- Leedcode算法专题训练(双指针)
算法思想 双指针 167. 两数之和 II - 输入有序数组 双指针的典型用法 如果两个指针指向元素的和 sum == target,那么得到要求的结果: 如果 sum > target,移动较 ...
随机推荐
- 4 weekend110的hdfs&mapreduce测试 + hdfs的实现机制初始 + hdfs的shell操作 + 无密登陆配置
Hdfs是根/目录,windows是每一个盘符, 1 从Linux里传一个到,hdfs里去 2 从hdfs里下一个到,linux里去 想从hdfs里,下载到linux, 涨知识,记住,hdfs是建 ...
- Y2K Accounting Bug
题目: Description Accounting for Computer Machinists (ACM) has sufferred from the Y2K bug and lost som ...
- ajax 小案例
ajax 异步提交数据,实现无刷新提交表单 ajax.html <!DOCTYPE html> <html> <head> <meta charset=&qu ...
- java.sql.SQLException: Before start of result set解决方法
java.sql.SQLException: Before start of result set解决方法 今天做东西的时候发现这个错误,查了查,特地记下来,以后开始积累了 哈哈 解决发法是: 使用r ...
- request对象多种方法封装表单数据
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, ...
- Oracle11g - dos 命令 sqlplus/nolog 提示 不是内部命令解决办法
继安装Oracle 11g后,解锁SCOtt时发现 dos 命令 sqlplus/nolog 提示 不是内部命令解决办法 通过实际验证现整理有效方法步骤如下: 步骤一:开始>>找到Or ...
- VIM中文乱码(_vimrc配置文件备份)
_vimrc在用户目录下: set fileencodings=ucs-bom,utf-,cp936,gb18030,big5,euc-jp,euc-kr,latin1 set encoding=ut ...
- burp
http://www.2cto.com/Article/201406/310929.html
- GPS定位,经纬度附近地点查询–C#实现方法
目前的工作是需要手机查找附近N米以内的商户,功能如下图 数据库中记录了商家在百度标注的经纬度(如:116.412007, 39.947545), 最初想法 以圆心点为中心点,对半径做循环,半径每增加 ...
- [GDI+] 生成缩略图的类文件SmallImage (转载)
直接看代码吧,大家可以直接复制使用 /// <summary> /// 类说明:SmallImage类, /// 编码日期:2012-08-20 /// 编 码 人: 苏飞 /// 联系方 ...