本周学习内容以搜素题目为中心进行,看了老师给的洛谷上的题目。在此总结一下题型和收获。
1、染色问题。在深搜中有一类这种问题,就是给你一个矩形阵列,这个矩阵可能由字符,整数等等组成。拿由整数来举例吧,让你去搜索一个在阵列中一个整数判断它周围上下左右的数是否符合题目要求。我认为可以把此类问题叫做染色问题,从开始进行搜索,每次搜索一个数将这个数进行染色(标记,可以让符合要求的标记为1,不符合要求的标记为0)。要注意边界的问题,我做的一个这种染色问题时,遇到第一个搜不下去的情况,可以再加一圈搜索从dfs(0,0)开始。
例如:P1506 拯救oibh总部 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<bits/stdc++.h>
using namespace std;
int xx[10]={0,0,-1,1};//xx[10],yy[10]进行搜索时,搜索位置上下左右的移动。
int yy[10]={1,-1,0,0};
int n,m,gs;
char ch[510][510];
void dfs(int x,int y)
{
if(x<0||y<0||x>n+1||y>m+1||ch[x][y]=='*')return;
ch[x][y]='*';//进行标记
for(int i=0; i<4; i++)
dfs(x+xx[i],y+yy[i]);//搜索这个数周围的上下左右的数
}
int main()
{
cin>>n>>m;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cin>>ch[i][j];
dfs(0,0);//多搜一圈
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
{
if(ch[i][j]=='0')//找到符合要求的,进行累加
{
gs++;
}
}
cout<<gs;
return 0;
}
这个题目可以说是简单的一个染色问题的题目,但是这类题目大多数都是一个这种搜索思路,不同的区别可能就是题目中对符合条件的值的判断不同或者还有其他要求等等。看到有一些用广搜的题目,迷宫等可以向四个方向移动的(坐标),也可以通过深搜进行。
2、全排列问题
给你几个不同的数进行全排列,通过深搜进行完成,相当于数学中排序问题,选出几个数进行排列,可以想成从n个元素中,选m个不同的元素,放到m个盒子里,每个盒子每次随机选一个放进去,放过的盒子和元素进行标记,避免重复。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,pd[100],used[100];//pd是判断是否用过这个数
void print()//输出函数
{
int i;
for(i=1;i<=n;i++)
printf("%5d",used[i]);
cout<<endl;
}
void dfs(int k)//深搜函数,当前是第k格
{
int i;
if(k==n) //填满了的时候
{
print();//输出当前解
return;
}
for(i=1;i<=n;i++)//1-n循环填数
{
if(!pd[i])//如果当前数没有用过
{
pd[i]=1;//标记一下
used[k+1]=i;//把这个数填入数组
dfs(k+1);//填下一个
pd[i]=0;//回溯
}
}
}
int main()
{
cin>>n;
dfs(0);//注从第0格开始
return 0;
}
偷懒:全排列问题,在算法中有一个函数可以直接进行排列next_permutation(start,end)。
3、背包问题
给定i个物品和一个容器背包,物品的重量是我w[i],价值为v[i]。选出若干物品放到容器里,在不超过背包重量和容器大小时,求价值最大的值。这个物品要么选要么不选,只需考虑这两种情况
#include<bits/stdc++.h>
using namespace std;
const int maxn=30;
int n,v,mv=0;//物品件数,背包容量v,最大价值mv
int w[maxn],c[maxn];//w[i]为每件物品的重量,c[i]为每件物品的价值
void DFS(int i,int sumM,int sumC)//i为当前处理的物品号
//sumW,和sumC分别为当前总重量和当前总价值
{
if(i==n)
{
return;//完成对n件物品的选择
}
DFS(i+1,sumM,sumC);//不选第i件物品
if(sumM+w[i]<=v)//加入第i件物品后未超过容量V,才能继续
{
if(sumC+c[i]>mv)
{
mv=sumC+c[i];
}
DFS(i+1,sumM+w[i],sumC+c[i]);//选第i件物品
}
}
int main()
{
scanf("%d%d",&n,&v);
for(int j=0;j<n;j++)
{
scanf("%d",&w[j]);//每件物品的重量
}
for(int j=0;j<n;j++)
{
scanf("%d",&c[j]);//每件物品的价值
}
DFS(0,0,0);//初始时为第0件物品,当前总重量和总价值为0
cout<<mv<<endl;
return 0;
}
深搜不仅仅可以解决以上问题,还可以解决n皇后问题、素数环、类似食物链问题等问题
BFS可以解决最短路或最小操作问题、迷宫问题等。
P2802 回家 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<bits/stdc++.h>
using namespace std;
int jz[20][20]={0};//存储原地图
struct NODE//一个节点,即一个状态
{
int x,y,bs,xl;//位置,步数与血量
}qc,fr;
int zl[4][2]={{0,1},{0,-1},{1,0},{-1,0}};//增量,即向四周可能走的位置
int visit[20][20]={0};//visit int版数组
queue<NODE>wz;//bfs队列
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int sx,sy;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&jz[i][j]);
if(jz[i][j]==2)sx=i,sy=j;//记录出发位置
}
wz.push((NODE){sx,sy,0,6});//推入队列,这里用了强制类型转换
visit[sx][sy]=6;//初始位置设置其visit值
bool tf=true;//判断是否完成任务,tf==false即完成
while(!wz.empty()&&tf)//直到完全失败或成功
{
qc=wz.front();//取队列最前面的节点
wz.pop();//弹出队列
if(qc.xl==1)continue;//如果血量为1,则继续
for(int i=0;i<4&&tf;i++)//四个方向
{
if(jz[qc.x+zl[i][0]][qc.y+zl[i][1]])//如果可以到达
if(visit[qc.x+zl[i][0]][qc.y+zl[i][1]]<qc.xl-1)//如果血量更大
{
fr.x=qc.x+zl[i][0];
fr.y=qc.y+zl[i][1];
fr.bs=qc.bs+1;//更新新的节点
fr.xl=jz[fr.x][fr.y]==4?6:qc.xl-1;//如果下一个节点是有鼠标的,那么有变成6
visit[fr.x][fr.y]=qc.xl-1;//更新visit
if(jz[fr.x][fr.y]==3)tf=false;//如果任务完成,那么tf更新
wz.push(fr);//加入队列
}
}
}
if(tf)printf("-1");//如果任务失败
else printf("%d",fr.bs);//如果任务成功,则输出结果
return 0;
}
对于dfs和bfd,在一些题目中两种搜索都可以使用,两种搜索的思路和侧重点不同,并且时间复杂程度不同,在代码中要注意剪枝,防止代码超时。现在,我能够理解进行判断深搜广搜问题的题目,但是对于写代码仍然写不出来,一些简单的题还是可以写一写,上了难度之后就感觉到迷糊不清,接下来还需要重复练习,重复理解大佬们的题解和思路。