菜j的算法学习笔记3.23

今儿来聊一聊回溯

回溯虽然很简单吧…好像都这么认为…但是在一些题目里面灵活运用好像还挺重要的,今儿就来看道题
这道题是洛谷的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级还剩仨月,过两天研究一下分组背包然后深究一下动态规划

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值