目录
学习篇(搜索)
先举个例子:遍历二叉树
(树的遍历层次)算法思想:维护一个队列,用于存放节点信息。当访问到一个节点的时候,先访问该节点,再将该节点出队,再将其左右儿子分别放入队列。
再来讲讲搜索:
起初我做搜索题真的是一头雾水,直接放弃
搜索的本质:暴力枚举(让程序从一个状态跳到另一个状态就是搜索。)
只要把问题,能够表示出他的状态,并且从一个状态变化到另一个状态的规则能够定义出来,就能够去搜索
再举个例子体会一下搜索
hdu 1548
题意:有一个奇怪的电梯,只有两个按钮:上,下,并且,当在i层的时候,按上就会上ki层,按下就会下ki层,电梯不高于N,也不低于1;问:当你再A层要去B层,至少要按上或下多少次。
状态转移: 把楼层编号和按键钮的次数的信息作为搜索的状态,再按照题目的意思/规则,在各个状态之间变化,这个状态之间的变化就叫状态转移。
(A层,0) ->···->( B层,x)
A层 到下一个状态有 上ki 或 下ki 并且次数加1
不难看出这一题的状态转移图是二叉树类型的
BFS
基本思想:
从初始状态开始,利用规则,生成所有可能的状态,构成树的下一层节点。
检查是否出现目标状态G,若未出现,就对该层所有状态节点,分别依次利用规则,生成再下一层的所有状态。
对新一层的所有状态节点继续检查是否出现G,若未出现,继续按照上面的思想继续生成下一层的所有节点,这样一层一层往下展开,直到目标状态出现为止。
**他的优化:**对状态作标记
板子:请看下面的题(前两题足够了
DFS
关键:解决“当下该如何做”!
至于下一步怎么做,则与当前一样,只是参数不一样而已
基本模型:
就是递归:题做多了就知道要相信自己的递归函数,不用去想下一层
水题集
BFS
hdu 1548:二叉树型搜索
#include <bits/stdc++.h>
#define ll long long
using namespace std;
//"二叉树型的状态转移"
int A,B,N,a[210],v[210];
struct node{int l,s;};//l楼层,s层数
void bfs(){
node cur,nex;
cur.l=A; cur.s=0;//初始化
queue<node>q;//创建队列
q.push(cur);//父节点入队
v[A]=1;//标记
while(!q.empty()){
cur=q.front();
q.pop();//队头出队
if(cur.l==B){//找到目标状态
cout<<cur.s<<endl;return;
}
//下一层状态1
nex.l=cur.l+a[cur.l];
nex.s=cur.s+1;
if(nex.l<=N && !v[nex.l]){//符合条件入队
q.push(nex);v[nex.l]=1;
}
//下一层状态2
nex.l=cur.l-a[cur.l];
nex.s=cur.s+1;
if(nex.l>=1 && !v[nex.l]){
q.push(nex);v[nex.l]=1;
}
}
cout<<"-1"<<endl;//无法到达目标状态
return ;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while(cin>>N){
if(N==0)break;
cin>>A>>B;
for(int i=1;i<=N;i++){cin>>a[i];v[i]=0;}//标记数组初始化
bfs();
}
return 0;
}
hdu 1459:三个杯子倒可乐
TLE 了一次(我感觉hdu的测评有点问题,应该是WA才对,之前也遇到这种情况):如果S为奇数的话,就直接输出-1.这种情况没考虑。
#include <bits/stdc++.h>
using namespace std;
int A,B,C,v[110][110],lev;//用二维数组标记就行了,三维也行
struct node {int a,b,c,s;};
void bfs(){
node cur,nex;
cur.a=A; cur.b=cur.c=cur.s=0; v[cur.a][cur.b]=1;//初始状态的信息输入
queue<node>q;//搞个队列
q.push(cur);//初始状态入队
while(!q.empty()){
cur=q.front();
q.pop();//父节点出队
if(cur.a==A/2&&A%2==0){//目标状态出现
cout<<cur.s<<endl;return ;
}
lev=B-cur.b;//a->b
if(cur.a&&lev){
if(cur.a>=lev){nex.a=cur.a-lev;nex.b=B;}
else {nex.a=0;nex.b=cur.a+cur.b;}
nex.c=cur.c;nex.s=cur.s+1;
if(!v[nex.a][nex.b]){
q.push(nex);v[nex.a][nex.b]=1;
}
}
lev=C-cur.c;//a->c
if(cur.a&&lev){
if(cur.a>=lev){nex.a=cur.a-lev;nex.c=C;}
else {nex.a=0;nex.c=cur.a+cur.c;}
nex.b=cur.b;nex.s=cur.s+1;
if(!v[nex.a][nex.b]){
q.push(nex);v[nex.a][nex.b]=1;
}
}
lev=B-cur.b;//c->b
if(cur.c&&lev){
if(cur.c>=lev){nex.c=cur.c-lev;nex.b=B;}
else {nex.c=0;nex.b=cur.c+cur.b;}
nex.a=cur.a;nex.s=cur.s+1;
if(!v[nex.a][nex.b]){
q.push(nex);v[nex.a][nex.b]=1;
}
}
lev=A-cur.a;//c->a
if(cur.c&&lev){
if(cur.c>=lev){nex.c=cur.c-lev;nex.a=A;}
else {nex.c=0;nex.a=cur.c+cur.a;}
nex.b=cur.b;nex.s=cur.s+1;
if(!v[nex.a][nex.b]){
q.push(nex);v[nex.a][nex.b]=1;
}
}
lev=C-cur.c;//b->c
if(cur.b&&lev){
if(cur.b>=lev){nex.b=cur.b-lev;nex.c=C;}
else {nex.b=0;nex.c=cur.b+cur.c;}
nex.a=cur.a;nex.s=cur.s+1;
if(!v[nex.a][nex.b]){
q.push(nex);v[nex.a][nex.b]=1;
}
}
lev=A-cur.a;//b->a
if(cur.b&&lev){
if(cur.b>=lev){nex.b=cur.b-lev;nex.a=A;}
else {nex.b=0;nex.a=cur.b+cur.a;}
nex.c=cur.c;nex.s=cur.s+1;
if(!v[nex.a][nex.b]){
q.push(nex);v[nex.a][nex.b]=1;
}
}
}
cout<<"NO"<<endl;return ;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while(cin>>A>>B>>C){
if(A==0&&B==0&&C==0)break;
memset(v,0,sizeof(v));
bfs();
}
return 0;
}
hdu 1372:骑士起点到终点最少步数
Output Limit Exceeded 了一次:while里直接scanf()啥也不加的情况,我再也不敢了
#include <bits/stdc++.h>
using namespace std;
//这里才发现 string s;这样的不能用scanf("%s")输入
int sx, sy, x, y, v[70][70]; char s1[5], s2[5];
int dy[8] = { 2,1,-1,-2,-2,-1,1,2 };
int dx[8] = { 1,2,2,1,-1,-2,-2,-1 };
struct node { int x, y, s; };
int bfs() {
node cur, nex;
cur.x = sx; cur.y = sy; cur.s = 0; v[cur.x][cur.y] = 1;
queue<node>q;
q.push(cur);
while (!q.empty()) {
cur = q.front();
q.pop();
if (cur.x == x && cur.y == y) {
return cur.s;
}
for (int i = 0; i < 8; i++) {
nex.x = cur.x + dx[i]; nex.y = cur.y + dy[i];
if (!v[nex.x][nex.y] && nex.x >= 1 && nex.x <= 8 && nex.y >= 1 && nex.y <= 8) {
nex.s = cur.s + 1; q.push(nex);
v[nex.x][nex.y] = 1;
}
}
}
}
int main() {//cin>>c>>a>>d>>b
//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while (scanf("%s%s", s1, s2) == 2) {//好坑啊这里,我习惯while里直接scanf别的什么都不加,结果一直Output Limit Exceeded
sy = s1[0] - 96; y = s2[0] - 96; sx = s1[1] - '0'; x = s2[1] - '0';
memset(v, 0, sizeof(v));
printf("To get from %s to %s takes %d knight moves.\n", s1, s2, bfs());
}
return 0;
}
hdu 1242:类似起点到终点题
#include <bits/stdc++.h>
using namespace std;
struct node{int x,y,t;};
int g[210][210],sx,sy,x,y,v[210][210],n,m;
int dy[4]={1,0,-1,0};
int dx[4]={0,1,0,-1};
void bfs(){
node cur,nex;
cur.x=sx;cur.y=sy;cur.t=0;v[cur.x][cur.y]=1;
queue<node>q;
q.push(cur);
while(!q.empty()){
cur=q.front();
q.pop();
if(cur.x==x&&cur.y==y){
cout<<cur.t<<endl;return ;
}
for(int i=0;i<4;i++){
nex.x=cur.x+dx[i];nex.y=cur.y+dy[i];
if(g[nex.x][nex.y]=='#'||nex.x>n||nex.x<1||nex.y<1||nex.y>m)continue;
nex.t=cur.t+1;
if(g[nex.x][nex.y]=='x')nex.t+=1;
if(!v[nex.x][nex.y]){
q.push(nex);v[nex.x][nex.y]=1;
}
}
}
cout<<"Poor ANGEL has to stay in the prison all his life."<<endl;return;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while(cin>>n>>m){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char a;cin>>a;
g[i][j]=a;
if(a=='a'){x=i;y=j;}
if(a=='r'){sx=i;sy=j;}
v[i][j]=0;
}
}
bfs();
}
return 0;
}
hdu 1253:类起点到终点(三维)
wa了一次:没考虑到他有可能永远也到不了出口的情况
#include <bits/stdc++.h>
using namespace std;
int A,B,C,T;
int g[60][60][60],v[60][60][60];
int da[6]={0,1, 0,-1,0, 0};
int db[6]={1,0,-1, 0,0, 0};
int dc[6]={0,0, 0, 0,1,-1};
struct node {int a,b,c,t;};
void bfs(){
node cur,nex;
cur.a=cur.b=cur.c=cur.t=0;v[cur.a][cur.b][cur.c]=1;
queue<node>q;
q.push(cur);
while(!q.empty()){
cur=q.front();
q.pop();
if(cur.a==A-1&&cur.b==B-1&&cur.c==C-1){
printf("%d\n",(cur.t<=T?cur.t:-1));return;
}
for(int i=0;i<6;i++){
nex.a=cur.a+da[i];nex.b=cur.b+db[i];
nex.c=cur.c+dc[i];nex.t=cur.t+1;
if( nex.a<A&&nex.a>=0&&nex.b<B&&nex.b>=0&&nex.c>=0&&nex.c<C && !v[nex.a][nex.b][nex.c] && g[nex.a][nex.b][nex.c]==0 ){
q.push(nex);v[nex.a][nex.b][nex.c]=1;
}
}
}
printf("-1\n");return ;
}
int main(){
int t;scanf("%d",&t);
while(t--){
scanf("%d%d%d%d",&A,&B,&C,&T);
for(int i=0;i<A;i++){
for(int j=0;j<B;j++){
for(int k=0;k<C;k++){
scanf("%d",&g[i][j][k]);
v[i][j][k]=0;
}
}
}
bfs();
}
return 0;
}
DFS
hdu 1241:油坑个数
题意:求相邻小油坑构成的大油坑个数
注意输入
#include <bits/stdc++.h>
using namespace std;
int n,m,ans;char mp[110][110];
int dx[8]={1,-1,0,0, -1,-1,1,1};
int dy[8]={0,0,1,-1, -1, 1,1,-1};
void dfs(int x,int y){
//dfs就是一层的状态;如果不知道出口是什么就想想最后一层
//最后一个状态一定是四周都是*,所以出口在循环完之后
for(int i=0;i<8;i++){
int tx=x+dx[i],ty=y+dy[i];
if(mp[tx][ty]=='*'||tx<1||ty<1||tx>m||ty>n)continue;
mp[tx][ty]='*';
dfs(tx,ty);//连通图不需要回溯
}
return ;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while(cin>>m>>n){
if(m==0)break;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
char a;cin>>a;
mp[i][j]=a;
}
}
ans=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(mp[i][j]=='@'){
mp[i][j]='*';
dfs(i,j);ans++;
}
}
}
cout<<ans<<endl;
}
return 0;
}
wa了多次
DFS
hdu 1010:迷宫类恰好到出口
题意:每走一步花费一秒,并且走过的格子不能再走,求是否能从起点恰好走到终点。
tle的原因:void的dfs中第一行的判断边界条件错了
re的原因:一些编译细节字母打错了(该死,找了好久才找到
难点:
剪枝:可以在dfs前剪枝
1、走完所有可走的路用的时间 小于 T -> NO
2、"两点最短的路径"用的时间 比 T大 -> NO
3、奇偶剪枝:
因为从起点到终点 的任何路径的步数的奇偶性相同(可以用“最短路”的值的奇偶性表示)
所以,若t秒 和 路径时间 的奇偶性不一致(同性相减为偶数) ->NO
#include <bits/stdc++.h>
using namespace std;
int N,M,T,sx,sy,x,y,mi;char mp[10][10];bool f;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
void dfs(int sx,int sy,int s){
if(sx>N||sy>M||sx<1||sy<1)return ;//这里是||啊
if(sx==x&&sy==y&&s==T) f=1;//结束条件
if(f) return;
for(int i=0;i<4;i++){
int tx=sx+dx[i],ty=sy+dy[i];
if(mp[tx][ty]=='X'||tx<=0||tx>N||ty<=0||ty>M)continue;
mp[tx][ty]='X';
dfs(tx,ty,s+1);
mp[tx][ty]='.';
}
return;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while(cin>>N>>M>>T){
if(M==0&&N==0&&T==0)break;
int wall=0;
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
char a;cin>>a;
mp[i][j]=a;
if(a=='S'){sx=i;sy=j;}
else if(a=='D'){x=i;y=j;}
else if(a=='X')wall++;
}
}
mi=abs(sy-y)+abs(sx-x);
if(N*M-wall<=T || mi>T || (T-mi)%2!=0 ){
cout<<"NO"<<endl;continue;
}
mp[sx][sy]='X';
f=0;
dfs(sx,sy,0);
if(f)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int N,M,T,sx,sy,x,y,mi,temp;char mp[10][10];bool f;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
bool dfs(int sx,int sy,int s){
if(sx==x&&sy==y&&s==T)return 1;
temp=T-s-abs(sx-x)-abs(sy-y);
if(temp<0||temp%2==1)return 0;
for(int i=0;i<4;i++){
int tx=sx+dx[i],ty=sy+dy[i];
if(mp[tx][ty]!='X'&&tx>0&&tx<=N&&ty>0&&ty<=M){
mp[tx][ty]='X';
if(dfs(tx,ty,s+1))return 1;
mp[tx][ty]='.';
}
}
return 0;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while(cin>>N>>M>>T){
if(M==0&&N==0&&T==0)break;
int wall=0;
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
char a;cin>>a;
mp[i][j]=a;
if(a=='S'){sx=i;sy=j;}
else if(a=='D'){x=i;y=j;}
else if(a=='X')wall++;
}
}
mi=abs(sy-y)+abs(sx-x);
if(N*M-wall<=T || mi>T || (T-mi)%2!=0 ){
cout<<"NO"<<endl;continue;
}
mp[sx][sy]='X';
if(dfs(sx,sy,0))cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}