今天网络赛又被虐了,哎,还是太菜,还是忘了吧,重要的是努力,是提升,黑暗总会过去的!!!希望一直都有,只要你肯坚持!从现在做起,重拾信心,重拾希望,不要企图一步登天,不要想那些虚无缥缈的未来,脚踏实地,向前进!
现在就把前阵子写的专题一做个总结吧。
bfs:递归实现,所以容易超时爆栈,而且判重、标记要么都在中间,要么都在开头(根据写法类型而定,“先判再走”or"走后再判")【见A题】。回溯恢复要么都在本层,要么都在下一层。【见A题】
如果题目要求最优解,找到目标后,每次还要进行比较取优。
如果题目中,每一步可以任意走的话,可以采用。
bfs:循环实现,不会爆栈。判重、标记要么都在入队前,要么都在出队后。
第一次找到目标后,就是最优解,无需比较取优。
如果题目中,每一步有规定,只能特定地走,可以采用。
可以打印路径,只要用数组模拟队列,并且在节点的结构体中加一个pre就行,路径的第一个节点的pre设为-1,打印时用一个递归函数,终止条件就是pre!=-1。
A题:
题意:
给你一个n*n的棋盘,棋盘上只有'#'位置可以放置棋子,要求放置k个棋子,棋子每一行,每一列只能有一个棋子。
思路:
由于n,k很小,不超过8,直接暴搜就行,看了一下自己的提交记录,发现并不是1A,任何的非1A都可能导致一场比赛的失败,这也暴露了自己对暴搜写法有一定的问题。
先看一下我的AC代码:
#include
我上面的AC代码就是典型的判断后再走。
再看一下我的失败代码:
#include
不难发现失败代码与ac代码就差别在对vis1数组的判重的位置,这告诉我,既然选择“判断后再走”,就必须严格遵循“先判重,然后走”,不能又在开头再判重。
另外,又参考了一下别人的代码:
#include
#include
#include
#include
using namespace std;
char a[10][10];
int ans=0;
int n,k;
int visit [10];
void bfs(int cnt,int x,int y)
{
if(cnt==k) {
ans++;
return ;
}
visit[y]=1;
for(int i=x+1;i
他的代码在恢复状态的写法与我不同(对visit数组),其实两种方法都可以,我的放在里面,就是等它回到原来这一层,再给恢复,而他的代码,则是在进入下一层再标记,然后在回到本层才恢复标记。
B题:
题意:
给一个三维的迷宫,’#‘不能走,’.'表示可以走,'S'表示起点,'E'表示终点,求最短逃离时间,若不能逃离,输出“Trapped!”,否则输出“Escaped in x minute(s).”。
思路:对于每一步行走有限制的题目,一般采用bfs,而每一步可以任意行走的题,例如上一题,可以采用dfs,本次采用bfs,对六个方向进行试探,入队...
代码1(极其冗杂易错的代码):
#include
其实上面的bfs的代码量可以缩短很多的,首先可以写一个check函数,判断点是否合理,另外,六个方向可以用三个一维数组(1*6)表示。
代码2(简洁代码,其中的六个方向的表示,代码中提供了两种方法,个人喜欢没被注释掉的那种方法):
#include
C题
题意:
在一个一维空间里,给你一个初始位置,以及一个目标位置,你有三种方式可以走,1是左移一个单位,2是右移一个单位,3是位置扩为两倍,要求位置在[0,1e5]之间。问到达目标位置最少需要多少步?
思路:
限定了每步只可有三种选择,那么选择dfs,再注意一下边界就行。
代码:
#include
D题:
题意:
给N*M的01矩阵,对每一个位置,可以进行01翻转,翻转的同时,它的上下左右的四个位置也同时进行01翻转。问最少需要多少次翻转,可以使矩阵全0,输出每个位置的翻转次数。
思路:
首先,明确一点,每一个位置翻转2次,将回到原状态,翻转3次,等同于翻转1次,因而每个位置最多翻转1次。
另外,对于这种翻转问题,而且是对周围的格子有影响的翻转,一般并不需要确定全部格子,只需确定一部分格子,剩下的格子在确定好了的格子的影响下,也将被确定。而对于本题,你只要确定第一行的格子,那么第二行也就确定了(第二行的分布方式得需将第一行变为全0,就是说第一行的第i列为1时,相应的第二行的第i列要翻转一次),同理,第二行确定后,第三行受第二行的约束,也将确定......所以只要枚举第一行就行,可以采用dfs,暴力枚举第一行,对于第一行的每一个位置,进行01试探。
代码:
#include
E题:
题意:
给你一个数n,要求输出一个n的倍数m,其中m是只有0和1的十进制数。
思路:
这题的n是不超过200,而m不超过100位,也就是10^(100),当时看了就慌了,这个m没法用任何类型来存啊。。太大了啊。。。后来想想,n不超过200,那么相应的m应该也不会达到100位吧,就用long long试试,然后就过了。。。
由于m只有0和1,那么对于m的每一位进行01试探就行。DFS和BFS都行吧。
我用的bfs(双端口bfs),另外,本题是special judge,也就是可能存在多解,输出一个就行,所以输出结果和样例不同也没事。
代码:
#include
F题:
题意:
有a,b两个质数,要求通过一些转换,使a变为b,每次变换,只能变换一位,而且变换后的数也得是质数,问最少变换几次,可以得到b。a,b都是四位数。
思路:
由于只有四位数,然后每次只能改变一位数,那么采用bfs,可以对每一位的数依次尝试0~9,一共36种尝试,如果改变一位数后是质数,就入队,直到找到b。
注意点:
由于要对每一位进行改变,所以采用字符数组来存数字,方便对每一位进行操作,然后还得把字符数组表示的数字转化为数字,来判断是否是质数。所以要写一个字符数组的函数:
int chartonum(char s[]){
int num = 0;
for(int i = 0; i < 4; ++i){ //一个数字的越前位,越大,所以正向处理
num = num*10 + (s[i]-'0');
}
return num;
}
代码:
#include
G题:
题意:
类似于洗牌,给你两个字符串s1,s2,将s1,s2交叉插入,形成一个新字符串s,问能否得到给定的字符串。
思路:
由于整个过程都是固定唯一的,可以直接模拟这个过程,每次操作完,判断是否达到要求就行。其实就是一个递归函数,算不上dfs,好吧好吧,其实dfs不就是暴力递归吗。。。不过有一个问题就是,既然是递归,那么总得有一个终止条件吧,手算发现,经过一定次数的操作之后,s1,s2会回到一开始的s1,s2,那么以此作为终止条件就行。
技巧:
将s分割为s1和s2时,采用了strncpy函数。strncpy(字符数组名称,起始位置,切割长度)
代码:
#include
H题:
题意:
给你两个容器,分别能装下A升水和B升水,并且可以进行以下操作
FILL(i) 将第i个容器从水龙头里装满(1 ≤ i ≤ 2);
DROP(i) 将第i个容器抽干
POUR(i,j) 将第i个容器里的水倒入第j个容器(这次操作结束后产生两种结果,一是第j个容器倒满并且第i个容器依旧有剩余,二是第i个容器里的水全部倒入j中,第i个容器为空)
现在要求你写一个程序,来找出能使其中任何一个容器里的水恰好有C升,找出最少操作数并给出操作过程
思路:
在结构体中设置两个变量来表示两个瓶子中当前的水量。用bfs来遍历所有的可能,每一步有三种可能的操作。
另外由于要输出路径,所以要用数组模拟队列,并在结构体中设置了pre,路径中的第一个节点,也就是第一个入队的节点,将其的pre设为-1,之后入队的节点的pre设为父节点的数组下标。最后用递归函数打印路径,递归的终止条件是pre==-1。
还有就是由于选择第一种操作两种可能,所以进入循环前,要先push两个节点,也就是双入口bfs。
代码:
#include
I题:
题意:
两个熊孩子在n*m的平地上放火玩,#表示草,两个熊孩子分别选一个#格子点火,火可以向上向下向左向右在有草的格子蔓延,点火的地方时间为0,蔓延至下一格的时间依次加一。求烧完所有的草需要的最少时间。如不能烧完输出-1。
思路:
思路很简单,这题明显是双入口的bfs。先把可以烧的位置存进来,然后一个二重循环,遍历bfs 两个入口的位置,找出最优解就行。(这里要比较取优的原因在于,要进行多个bfs,每一个bfs求得的是最优解,然后在这些个最优解中取优)。
代码:
#include
K题:
题意:
定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
思路:
很简单的bfs迷宫问题,由于要打印路径,得模拟队列+pre标记。
代码:
#include
L题:
题意:
给你一个字符矩阵,’*‘表示没油,’@‘表示有油,求图中油田的个数。
思路:
其实就是求连通块个数,直接用dfs搜一遍就行(遍历字符数组的每一个位置,如果是'@’,油田个数加一,再dfs其连通的'@',并做上标记)。
代码:
#include
M题:
题意:
只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。问两个人喝能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。
思路:
和H题一样的题,只不过这里是3个容器,所以结构体需要三个变量分别记录三个容器剩下的可乐。这里不需要打印,不需要模拟队列,直接用STL中的queue就行。
代码:
#include
N题:
题意:
一张图,上面有'@',两个人约定到其中的一个'@'中见面,求两人见面所花的最短时间。
思路:
可以求出两个人到达每一个'@'所花的时间,用两次bfs就行,然后对于每个'@',统计两人花费的时间(即两人分别所花时间相加),取最小值就行。
注意点:
bfs的终止条件是遍历完所有的'@',所以事先要记录图中'@'的个数。
代码:
#include