本周接着看搜索专题,大部分题读过一遍,理解了题意,也试着解题。前面的题比较基础,开始做还有些困难,做的熟练了,速度也就跟了上来。后面的题就难了很多,不再是单纯的深搜广搜,多了些与其他知识点的结合,如二叉树、STL、并查集等。难度加大,做题速度也慢了下来。虽然出的题不算多,但收获不少,在此整理一些我觉得很有收获的题目。
1.P1506 拯救oibh总部.
这道题题意是给出由‘* ’和‘0’组成的图,求上下左右被‘*’包围的0有多少个。明显是连通块问题,一般的解法是从第一格向最后一格遍历寻找目标点,但是通过浏览题解,我学到了一个从外围向内搜索的方法。
从外向内搜索的题解.
根据题意,外围的0没有围墙,水一来就会被淹,所以从外围开始向内圈遍历比从第一格向最后一格遍历要简单许多,而且题解用两个for循环了第1,x行和第1,y列从而达到从外围开始遍历的做法也让我开拓了思路,这对我以后解决类似题有很大帮助。所以有时做题不能僵硬地套用模板,要学会结合题目灵活变通,同时思维要活跃,做题时要常常思考这种方法合适吗?有没有更有的解法?这样不断训练才可能提升我们快速找到最优解的能力。
2 P1030 NOIP2001 普及组 求先序排列.
这道题为二叉树与搜索结合的题目,题意为根据二叉树的中序与后序排列求出它的先序排列。
在读题时看到了先序排列等概念,开始对此概念不甚熟悉,后来通过查阅资料也理解了概念,在此将之作为知识点整理一下。
- 先序排列:先序是先访问树的根节点,再访问树的左子节点,再访问右子节点。
- 中序排列:中序是先访问树的左子节点,再访问树的根节点,再访问右子节点。
- 后序排列:后序先访问树的左子节点,再访问树的右子节点,再访问根节点。
思路:
以中序排列ACTRBHFKX,后序排列CARTHXKFB为例,可得B为根节点,ACTR为左子树,HFKX为右子树;
那么怎么确定子树的结点位置呢?由后序CART得子树的根节点为T,子树的左子树为AC,子树的右子树为R,以此类推,可最终求得明确排序的二叉树,从而求得先序排列。
题解:
1.根据后序排列找到根节点;
2.根据中序排列找到左右子树;
3.递归重复1.2步骤找到子树的排列方式。
一点新东西:
- string类中substr( )函数
复制子字符串,要求从指定位置开始,并具有指定的长度。
(用这个函数解题真的嘎嘎快)
核心代码:
void dfs(string in,string post)
{ char ch=post[post.size()-1];//ch存放根节点
cout<<ch;
int k=in.find(ch);//根节点在中序排列中的位置
preod(in.substr(0,k),postod.substr(0,k));
preod(in.substr(k+1),postod.substr(k,in.size()-k-1));//递归左右子树
}
- P1341 无序字母对.
给定N个各不相同的无序字母对,输出一个字典序最小的有(n+1)个字母的字符串使得每个字母对都出现。读题发现是欧拉回路问题。在写题前,我们先理解一下字典序和欧拉回路。
- 字典序:简单来说就是按照字典中出现的先后顺序进行排序。
- 欧拉回路:在一个图中,由i点出发,将每个边遍历一次最终到达j点的一条路径叫欧拉路径,i=j时的欧拉路径即为欧拉回路。
- 判断欧拉路径是否存在:
无向图:每个点的度数均为偶数。
有向图:所有点的入度等于出度,就存在一条欧拉回路。
(参照此博客:欧拉回路/路径【总结】)
知道了这些,我们就可以看题啦。本题实际上就是在找欧拉路经,每个字符可以看做图中的一个点,输入的字符串可以看做两点之间有连通,求输出的字符串实际上就是找输出一笔画回路,至此思路就明了了·。
代码
#include<bits/stdc++.h>
#define N 10010
using namespace std;
int n,m,d[N][N],s1=N,ans;//d是表示两点的连接
char du[N],a[N];//du存度数,a存路径
void search(int i)
{ for(int j=1;j<=150;j++)
if(d[i][j]>0){d[i][j]--; d[j][i]--; search(j);}
a[ans]=i;
ans++;
return ;
}
int main()
{ cin>>m;
for(int i=1;i<=m;i++)
{ string s;
cin>>s;
d[s[0]][s[1]]++;
d[s[1]][s[0]]++;
du[s[0]]++;
du[s[1]]++;
}
int c=0,h=0;
for(int i=1;i<=150;i++)
if(du[i]&1) {c++;if(!h)h=i;}
if(!h)
for(int i=0;i<150;i++) if(du[i]){h=i;break;}
if(c&&c!=2) {cout<<"No Solution";return 0;}
search(h);
if(ans<m+1){return 0;}
for(int i=ans;i>=1;i--) cout<<a[i];
return 0;
}
4.总结
这周看了许多搜索题,可谓收获满满。有的题能读懂题意,也有解题的思路,但总是在细节处出错,这点还需要加以改进;有的题比较难,时常会找不到思路,也经历过一道题卡好几天A不出来的煎熬。但更多的是A题之后的满足感和学到新东西的快乐。并且洛谷的题解区是个解答疑惑的好去处,一般遇到的困难都有人在上头讲解,还能接触许多别人的好的做题方法。养成做完题翻翻题解的习惯,开拓一下思维,相信能进步的更快。下周再接再厉!