写给未来大二的自己:结合注释!结合注释!结合注释!别把今天理解完的代码第二天就看不懂了啊喂!
图就长这样
//写在开头:图的相邻矩阵存储法中 1表示有边(有边=还有路可以到除这个点以外的别的点)
// 0表示自己到自己(即i=j),∞(正无穷)表示没有边
//先看main函数
//和《啊哈算法》有点不同的是书中的代码表示的图是一行一行遍历的,我出于习惯是一列一列遍历的
#include<stdio.h>
int book[101],sum,m,e[101][101];
void dfs(int cur)
{
int i;
printf("%d",cur);
sum++;//每访问一个顶点sum就加1
if(sum==m)
return;//当sum=m(此列所有点都访问过了)就可以直接退出了这个单列循环
for(i=1;i<=m;i++)//从一号顶点开始依次尝试,看哪些顶点与当前cur有边相连
{//从图中可以看出与1有边的有2,3和5,走哪个呢,此时我们规定一个顺序,按1~n走,于是先走2
//到了2发现接下来只能走4,接下来没有路了所以我们返回了刚刚出现多种路的分岔口
//同理走1-3,3-5,接着1-5
if(e[i][cur]==1&&book[i]==0)//不撞南墙(这里的南墙指的i=m时)不回头
{
book[i]=1;
dfs(i);
}
}
return;//深度优先一定要记得返回,一定要返回一定要返回,返回到分岔口才能再次开始
}
int main()
{
int i,j,n,a,b;
scanf("%d %d",&n,&m);//画一个n*m的图
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)//这是在画图
if(i==j) e[i][j]=0;//主对角线上都是0(自己到自己都是0)
else e[i][j]=99999999;//其他地方先放上∞
for(i=1;i<=m;i++)
{
scanf("%d %d",&a,&b);
e[a][b]=1;//遍历m遍,将其中有边的用1覆盖之前的∞
e[b][a]=1;
}
book[1]=1;//从一号顶点出发,标记1号顶点
dfs(1);
return 0;
}
输入与输出
看注释吧少女,是个菜狗都看得懂(没错就是我!)
别删book数组,虽然理解起来觉得没必要,但是一删就崩,我也不知道为什么。
还有深度优先一定要记得函数的返回(return;)这个很重要
接下来做一个题目深化一下
题目描述:有五个城市,它们之间的路程与能否通行看下图,求从城市1到城市5的最短路径
先画出它的图
接下来就是绘制图,不能到达的用∞表示,能达到的标注路程,自己到自己为0
PS:出于二维数组的特性a[行][列],这幅图包括接下来的代码为横向深度遍历
#include<stdio.h>
int min=99999999,book[101],n,e[101][101];
void dfs(int cur,int dis)//cur是目前到达的城市,dis是已走路程
{
int j;
if(dis>min) return;//如果当前走过的路程已经大于已有的最短路径就舍弃这条路
//这个可以运算缩短时间,特别是对于有很多种路径的图(不是此题)
if(cur==n)//如果到达目的地了
{
if(dis<min)
min=dis;
return;
}
for(j=1;j<=n;j++)
{
if(e[cur][j]!=99999999&&book[j]==0)//如果此路可走(e[cur][j]不等于正无穷)且此路没走过
{
book[j]=1;//标记此路
dfs(j,dis+e[cur][j]);//接着走
book[j]=0;//一条路走到黑后回来分岔口将分岔口的标记取消
}
}
return;
}
int main()
{
int i,j,m,a,b,c;
scanf("%d %d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(i==j)
e[i][j]=0;
else e[i][j]=99999999;
for(i=1;i<=m;i++)
{
scanf("%d %d %d",&a,&b,&c);
e[a][b]=c;//表示有一条路可以从城市a到城市b,并且路程为c
}
book[1]=1;//1号城市已经在路径中了
dfs(1,0);
printf("%d",min);
return 0;
}
输入:
5 8
1 2 2
1 5 10
2 3 3
2 5 7
3 1 4
3 4 4
4 5 5
5 3 3
输出:
9