DFS能否取消标记?(例题总结)

最近一直在学习DFS,对于DFS一些问题有了新的体会,但是标记问题一直掌握的不是很好,写下来方便自己回顾。

//第一题:能否打通电话(保留标记)
/*
5 5
1 3
2 3
3 4
2 4
4 5
*/
#include <bits/stdc++.h>

using namespace std;
const int MAXN=55;
bool visit[MAXN];
vector<int>graph[MAXN];

void DFS(int u)//无出口,仅仅是为了判断visit,就能达到我们的要求了1 
{
	for(int i=0;i<graph[u].size();i++)
	{
		int v=graph[u][i];
		if(visit[v]) continue;
		visit[v]=true;
		DFS(v);
	}
	return ;
}

int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		memset(graph,0,sizeof(graph));
		memset(visit,false,sizeof(visit));
		for(int i=0;i<m;i++)
		{
			int from,to;
			scanf("%d%d",&from,&to);
			graph[from].push_back(to);
		}
		DFS(1);
		if(visit[n]==true) cout<<"YES"<<endl;
		else cout<<"NO"<<endl; 
	}
	return 0;
}

//第二题:全排列问题:要清除标记:因为不同的次序是算不同的结果的
#include <bits/stdc++.h>//效率很低啊兄弟!! 

using namespace std;
bool visit[100];
string str;
string answer;
void DFS(int n)
{
	if(n==str.size()) 
	{
		cout<<answer<<endl;
		return ;
	}
	for(int i=0;i<str.size();i++)
	{
		if(visit[i]) continue; 
		visit[i]=true;
		answer[n]=str[i];
		DFS(n+1);
		visit[i]=false;
	}
	return ;
}

int main()
{
	while(cin>>str)
	{
		sort(str.begin(),str.end());
		answer=str; //为何要赋值,其实就是想要和str等长而已,因为有长度的string才可以进行数组赋值!! 
		memset(visit,false,sizeof(visit));
		DFS(0);	
	}	
	return 0;
} 

//第三题:神奇的口袋:要清除标记:要判断所有种方法,并且必须是不同次序的,position很重要
#include <bits/stdc++.h>//注意:这题一定要设置position!平时我们是判断is or no,用position为了状态累加节省时间
//求次数肯定是要清除标号的!如:1+3;1+2;这样不清除的话就结束了!因为2+3都已被访问,不能算了!
using namespace std;
int arr[50];
int count1=0;
int n;
int visit[50];
void DFS(int sum,int position)
{
    if(sum==40)
    {
        count1++;
        return ;
    }
    for(int i=position;i<n;i++)
    {
        if(sum+arr[i]>40||visit[i])
        {
            continue;
        }
        visit[i]=1;
        DFS(sum+arr[i],i+1);
        visit[i]=0;
    }
    return ;
}

int main()
{
    while(cin>>n)
    {
        memset(visit,0,sizeof(visit));
        memset(arr,0,sizeof(arr));
        for(int i=0;i<n;i++)
        {
            cin>>arr[i];
        }
        DFS(0,0);//不能固定起点使得visit【0】=true,因为起点不一定可行.
        cout<<count1<<endl;
    }
    return 0;
}
//有几块图像区域?要保留标记:因为从不同的点出发,可以形成多块区域,而我们仅要一种
#include <bits/stdc++.h>

using namespace std;
int visit[105][105];
int data[105][105];
int p,q,r;
int direction[8][2]={
	{1,1},{-1,0},{-1,-1},{-1,1},{0,1},{0,-1},{1,0},{1,-1},
}; 

void DFS(int x,int y,int value)//这个DFS的作用就是在给标记 
{
	for(int i=0;i<8;i++)//作用:给标记! 
	{
		int nx=x+direction[i][0];
		int ny=y+direction[i][1];
		if(nx<0||nx>=p||ny<0||ny>=q||visit[nx][ny]||abs(data[nx][ny]-value)>r) continue;  
		//这里很容易错!我们之前给的  value值,是data[x][y],那么到下面更新后就是两次变化的的数组比较!必错! 
		visit[nx][ny]=1; 
		DFS(nx,ny,data[nx][ny]);//和旧的data比较! 注意,value值是新的值! 
	}
	return ; 
} 

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(data,0,sizeof(data));
		memset(visit,0,sizeof(visit));
		scanf("%d%d%d",&p,&q,&r);
		for(int i=0;i<p;i++)
		{
			for(int j=0;j<q;j++)
			{
				scanf("%d",&data[i][j]);
			}
		}
		int count=0;
		for(int i=0;i<p;i++)
		{
			for(int j=0;j<q;j++)
			{
				if(!visit[i][j])//这句是核心啊 !  
				{
					visit[i][j]=1;
					DFS(i,j,data[i][j]);
					count++;
				}
			}
		 }
		 cout<<count<<endl; 
	}
	return 0; 
}

小结:图像问题和能否打通就是属于同一类,是按照visit这一外部条件来判断结果,因此DFS是void型,并且一旦清除标记就会导致结果的错误。全排列也是void,因为没有错误结果.。而神奇的口袋也是用外部变量count来判断的。因此也是void.
而另外的

//骑士能否走完所有位置问题
#include <bits/stdc++.h>

using namespace std;
const int MAXN=100;
bool visit[MAXN][MAXN];
int p,q;
int direction[8][2]={
    {-1,-2},{1,-2},{-2,-1},{2,-1},{-2,1},{2,1},{-1,2},{1,2} 
};

bool DFS(int x,int y,int step)//前两个元素是不可或缺的,帮助DFS栈的变化,后面的元素全是为了判断出口和结果啊
//很多递归都是这样:分为两个过程:过程递归参数+结果值参数   (有时候递归参数也是结果参数,如在BFS中) 
{
	if(step==p*q)
	{
		return true;
	}
	for(int i=0;i<8;++i)
	{
		int nx=x+direction[i][0]; //第一列控制 x 的变化
		int ny=y+direction[i][1];//第二列控制 y 的变化
		if(nx>=p||nx<0||ny>=q||ny<0||visit[nx][ny]) continue;
		visit[nx][ny]=true;//这样的话,就变成了能不能走通的问题;只要有一条能走通,我们肯定能钻到那 !  
		if(DFS(nx,ny,step+1)) return true;//没有返回true的话,就会输出false了!因为递归的流程决定!  	
	} 
	return false;
}

int main()
{
	while(cin>>p>>q)
	{
		memset(visit,false,sizeof(visit));
		visit[0][0]=true;//固定起点了,咱们就从一走了! 
		if(!DFS(0,0,1)) cout<<"No"<<endl;
		else cout<<"Yes"<<endl;
	}
	return 0;
}

这题的话,内部参数step判断出口,要用bool,更方便。并且本题的标记是保留的,因为我能走完所有的就可以啦。

发布了8 篇原创文章 · 获赞 0 · 访问量 65

猜你喜欢

转载自blog.csdn.net/weixin_46274692/article/details/105068292