图论的一些基本概念及实战
基本概念
图就是有N个顶点和M条边组成的集合。图分为有向图和无向图,如果给图的每条边规定一个方向,那么得到的图称为有向图,其他边也称为有向边。在有向图中,与一个点相关联的边有出边和入边之分,而与一个有向边关联的两个点也有始点和终点之分。边没有方向的图称为无向图。存储图的方式用邻接矩阵。
主要思想
首先是深度优先遍历,它的主要思想是:
首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点;当没有未访问过的顶点时,则回到上一个顶点,继续试探访问别的顶点,直到所有的顶点都被访问过。即沿着图的某一条分支遍历直到末端,然后回溯,再沿着另一条分支进行同样的遍历,直到所有的顶点都被访问为止。
接着是广度优先遍历,它的主要思想是:
首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。
实战
例题1-无向图的广深优先遍历
利用邻接矩阵存储图,分别用深度优先遍历和广度优先遍历图。
代码实现:
#include<bits/stdc++.h>
using namespace std;
int n,m,a,b; //n为点,m为边
int e[51][51],book[51];
int ans;
void dfs(int cur)
{
printf("%d ",cur);
ans++;
if(ans==5) return;
for(int i=1;i<=n;i++){
if(e[cur][i]==1&&book[i]==0){
book[i]=1;
dfs(i);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) e[i][j]=0;
else e[i][j]=99999999;
}
}
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
e[a][b]=1;
e[b][a]=1;
}
book[1]=1;
dfs(1);
return 0;
}
结果:
代码实现:
#include<bits/stdc++.h>
using namespace std;
int n,m,a,b,head=1,tail=1;
int e[51][51],book[51],que[2501];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) e[i][j]=0;
else e[i][j]=99999999;
}
}
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
e[a][b]=1;
e[b][a]=1;
}
que[tail]=1;
book[que[tail]]=1;
tail++;
while(head<tail){
int cur=que[head];
for(int i=1;i<=n;i++){
if(e[cur][i]==1&&book[i]==0){
book[i]=1;
que[tail]=i;
tail++;
}
}
head++;
if(tail>n) break;
}
for(int i=1;i<tail;i++){
printf("%d ",que[i]);
}
return 0;
}
结果:
例题2–有向图的深度优先遍历(城市地图)
代码实现:
#include<bits/stdc++.h>
using namespace std;
int n,m,a,b,c;
int e[51][51],book[51];
int mi=99999999,ans;
void dfs(int cur)
{
if(cur==n){
// printf("%d\n",ans);
if(mi>ans) mi=ans;
return;
}
for(int i=1;i<=n;i++){
if(e[cur][i]!=0&&e[cur][i]!=99999999&&book[i]==0){
book[i]=1;
ans+=e[cur][i];
dfs(i);
book[i]=0;
ans-=e[cur][i];
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) e[i][j]=0;
else e[i][j]=99999999;
}
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
e[a][b]=c;
}
book[1]=1;
dfs(1);
printf("%d",mi);
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
*/
例题3–最少转机
首先是深度优先遍历,代码实现:
#include<bits/stdc++.h>
using namespace std;
int n,m,a,b,p,q;
int e[51][51],book[51];
int mi=99999999,ans;
void dfs(int cur)
{
if(cur==q){
if(mi>ans) mi=ans;
return;
}
for(int i=1;i<=n;i++){
if(e[cur][i]==1&&book[i]==0){
book[i]=1;
ans++;
dfs(i);
ans--;
book[i]=0;
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&p,&q);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) e[i][j]=0;
else e[i][j]=99999999;
}
}
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
e[a][b]=1;
e[b][a]=1;
}
book[1]=1;
dfs(p);
printf("%d",mi);
return 0;
}
接着是广度优先遍历,代码实现:
#include<bits/stdc++.h>
using namespace std;
int n,m,p,q;
int a,b,head=1,tail=1,flag;
int e[51][51],book[51];
struct note{
int x;
int step;
}que[2501];
int main()
{
scanf("%d%d%d%d",&n,&m,&p,&q);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) e[i][j]=0;
else e[i][j]=99999999;
}
}
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
e[a][b]=1;
e[b][a]=1;
}
que[tail].x=p;
book[que[tail].x]=1;
que[tail].step=0;
tail++;
while(head<tail){
for(int i=1;i<=n;i++){
if(e[que[head].x][i]==1&&book[i]==0){
book[i]=1;
que[tail].x=i;
que[tail].step=que[head].step+1;
tail++;
}
if(que[tail-1].x==q){
flag=1;
break;
}
}
if(flag) break;
head++;
}
printf("%d",que[tail-1].step);
return 0;
}
好啦,图论到此正式入门啦!