今儿来聊一聊回溯
回溯虽然很简单吧…好像都这么认为…但是在一些题目里面灵活运用好像还挺重要的,今儿就来看道题
这道题是洛谷的P2196 [NOIP1996 提高组] 挖地雷
https://www.luogu.com.cn/problem/P2196
标签是动态规划和深搜,但是我做的时候是按照深搜来的,动态规划没去想…不知道动态规划好不好做,但是我们今天聊的是,回溯
思路挺简单,难的就是代码实现(毕竟自己还挺菜,所以我感觉代码实现对我来说比较困难),题目中说的不太清楚,实际上每个通道都是单向的,也就是单向图
思路如下:这是题目给的例子的图,就按这个来说一下思路吧
从1开始走,就是从1开始进行深搜,直到无路可走,每次走的时候都加上该点的地雷数,然后取最大值,思路通俗易懂,难到我的是这个路径输出,到最后看了题解才学会如何输出路径…
原本自己写的代码是没有用回溯的,只是自己写了一个函数,让visited[](如果该点走过则置为1)这个数组的值都归0,但是题解上的代码是在dfs之前让visited[]数组置1,dfs之后再归0,太神奇了!我怎么就没有想到呢,这样做不仅节省了一个函数,还能让写的代码看起来更舒服,题解上用的我没用到的地方还有一个就是如果走到了尽头之后,应该怎么做后续的操作,我想到了给solve函数设置int类型,每次结束都是走到尽头的一个结果,然后再main方法里面再进行最大值的寻找,但是没想到啊!题解上给出的是定义了一个maxx全局变量,下面这段代码让我豁然开朗
if(check(x))//check是检查还有没有路可以走,如果没有可以走的路,执行下面的操作
{
if(ans>maxv)//如果当前的地雷最大值大于之前求过的最大值,则进行下面操作
{
maxv=ans;
num=step;
for(int i=1;i<=step;i++)
ans01[i]=path[i];
}
return ;
}
对啊,每次到最后可以直接判断最大值然后进行操作嘛!
再就是这个路径问题,题解又在函数中给出了一个形参,step,表示走了几步了,然后又给了两个数组path和ans01,path数组用来记录行走的时候每一步的位置,而走到尽头的时候,如果地雷出现了史前最大值,把每一次的路径再赋值给ans01数组,最后直接输出ans01数组就可以了,太神奇了,我感觉不太好想…起码我是这样的…
到最后在我自己代码的基础上进行了修改(改了将近百分之6 70…)
最后AC的代码如下:
#include <iostream>
using namespace std;
int a[25],g[25][25],visited[25],path[25],N,ans01[25],num,maxv;
int Max(int a,int b){
return a>b?a:b;
}
int check(int x)
{
for(int i=1;i<=N;i++)
{
if(g[x][i]==1 && visited[i]==0) return 0;
}
return 1;
}
void dfs(int x,int step,int ans){
if(check(x))
{
if(maxv<ans)
{
maxv=ans;
num=step;
for(int i=1;i<=step;i++)
ans01[i]=path[i];
}
return ;
}
for(int i=1;i<=N;i++)
{
if(g[x][i]==1 && visited[i]==0)
{
visited[i]=1;
path[step+1]=i;
dfs(i,step+1,ans+a[i]);
visited[i]=0;
}
}
}
int main()
{
int t;
cin>>N;
for(int i=1;i<=N;i++){
cin>>a[i];
}
for(int i=1;i<=N-1;i++){
for(int j=i+1;j<=N;j++){
cin>>t;
if(t==1){
g[i][j]=1;
}
}
}
for(int i=1;i<=N;i++)
{
visited[i]=1;
path[1]=i;
dfs(i,1,a[i]);
visited[i]=0;
}
for(int i=1;i<=num;i++)
cout<<ans01[i]<<' ';
cout<<endl<<maxv;
return 0;
}
这是昨天做的一道题,都说回溯简单,以至于我不屑于做此类的题,但是如果要运用起来,却走不动路,今天花了15分钟写了一道简单的深搜回溯的题
题目链接:https://www.luogu.com.cn/problem/P1605
题目很简单,深搜,回溯
代码如下:
#include <iostream>
using namespace std;
int D[6][6],flag[6][6],num=0;
int SX,SY,FX,FY,N,M;
int dfs(int x,int y){
if(x==FX && y==FY){
num++;
}else{
flag[x][y]=1;
if(D[x+1][y]==0 &&flag[x+1][y]==0 && x+1<=N){
dfs(x+1,y);
flag[x+1][y]=0;
}
if(D[x][y+1]==0 &&flag[x][y+1]==0 && y+1<=M){
dfs(x,y+1);
flag[x][y+1]=0;
}
if(D[x-1][y]==0 &&flag[x-1][y]==0 && x-1>=1){
dfs(x-1,y);
flag[x-1][y]=0;
}
if(D[x][y-1]==0 &&flag[x][y-1]==0 && y-1>=1){
dfs(x,y-1);
flag[x][y-1]=0;
}
}
}
int main()
{
int T,X,Y;
cin>>N>>M>>T>>SX>>SY>>FX>>FY;
for(int i=1;i<=T;i++){
cin>>X>>Y;
D[X][Y]=-1;
}
dfs(SX,SY);
cout<<num;
return 0;
}
自我感觉有点繁琐,之后看了看别人的题解,别人是用
int dx[4]={0,0,1,-1};//打表;
int dy[4]={-1,1,0,0};//打表;
这样然后结合循环进行的搜索遍历,但是作为新手来说,我的办法比较通俗易懂,嘿嘿嘿,其中上道题中学到的回溯进行了活学活用
if(D[x][y+1]==0 &&flag[x][y+1]==0 && y+1<=M){
dfs(x,y+1);
flag[x][y+1]=0;//回溯!!!!!!!!!!!
}
今儿就浅谈一下回溯,虽然很简单,但是活学活用还是比较复杂的,还是需要把根基筑牢,不说了,去背单词了,6级还剩仨月,过两天研究一下分组背包然后深究一下动态规划