最近一直在学习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,更方便。并且本题的标记是保留的,因为我能走完所有的就可以啦。