图论基础————搜索
        
          DFS 深度优先搜索
      原理简述:搜到底为止,不撞南墙不回头。
        
          例题
      输出全排列
八皇后问题
        
          BFS 宽度优先搜索
      原理简述:层层扩展,广撒网然后慢慢扩展出去。
每次搜到的都是离当前点最近的,具有一个“最短路“的性质。
        
          例题
      迷宫
        
          acwing845 八数码问题
      在一个3×3的网格中,1~8这8个数字和一个“x”恰好不重不漏地分布在这3×3的网格中。
例如:
在游戏过程中,可以把“x”与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
例如,示例中图形就可以通过让“x”先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3
   | 1 2 3   1 2 3   1 2 3   1 2 3 x 4 6   4 x 6   4 5 6   4 5 6 7 5 8   7 5 8   7 x 8   7 8 x
   | 
 
现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
        
          输入格式
      输入占一行,将3×3的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
        
          输出格式
      输出占一行,包含一个整数,表示最少交换次数。
如果不存在解决方案,则输出”-1”。
        
          输入样例:
      
        
          输出样例
      
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | #include<iostream> #include<algorithm> #include<queue> #include<unordered_map> #include<cstring> using namespace std; int n,m; int dx[]={-1,0,1,0}; int dy[]={0,1,0,-1}; int bfs(string start) {     string end="12345678x";     queue<string> q;     unordered_map<string,int> d;     q.push(start);     while(!q.empty())     {         auto t=q.front();         q.pop();         int dis=d[t];         if(t==end)return dis;
                   int k=t.find('x');         int x,y;         x=k/3, y=k%3;         for(int i=0;i<4;i++)         {             int tx=x+dx[i],ty=y+dy[i];             if(tx>=0 and ty>=0 and tx<3 and ty<3)             {                 swap(t[k],t[tx*3+ty]);                 if(!d.count(t))                 {                     d[t]=dis+1;                     q.push(t);                 }                 swap(t[k],t[tx*3+ty]);             }         }     }     return -1; } int main() {     string start;     for(int i=1;i<=9;i++)     {         char str;         cin>>str;         start+=str;     }     cout<<bfs(start);     return 0; }
   | 
 
        
          树与图的深度优先遍历
      
        
          例题
      
        
          acwing846.树的重心
      给定一颗树,树中包含n个结点(编号1~n)和n-1条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
        
          输入格式
      第一行包含整数n,表示树的结点数。
接下来n-1行,每行包含两个整数a和b,表示点a和点b之间存在一条边。
        
          输出格式
      输出一个整数m,表示将重心删除后,剩余各个连通块中点数的最大值。
        
          数据范围
      1≤n≤10^5
        
          输入样例
      9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
        
          输出样例:
      4
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
   | #include<iostream> #include<cstring> #include<cstdio> using namespace std; const int N=100050; int n; struct node {     int fr,to,next;  }e[N*2]; int head[N*2]; int vis[N]; int cnt=0; int ans=N; void add(int u,int v) {     e[++cnt].fr=u;     e[cnt].to=v;     e[cnt].next=head[u];     head[u]=cnt; } int dfs(int u) {     vis[u]=1;     int res=0,sum=1;     for(int i=head[u];i;i=e[i].next)     {         int to=e[i].to;         if(!vis[to])         {             int s=dfs(to);             res=max(res,s);             sum+=s;         }     }     res=max(res,n-sum);     ans=min(ans,res);     return sum; }    int main() {     ios::sync_with_stdio(0);     cin>>n;     for(int i=1;i<=n-1;i++)     {         int a,b;         cin>>a>>b;         add(a,b);         add(b,a);     }     dfs(1);     cout<<ans;     return 0; }
   | 
 
        
          树与图的宽度优先遍历
      
        
          例题
      
        
          acwing847.图中点的层次
      给定一个n个点m条边的有向图,图中可能存在重边和自环。
所有边的长度都是1,点的编号为1~n。
请你求出1号点到n号点的最短距离,如果从1号点无法走到n号点,输出-1。
        
          输入格式
      第一行包含两个整数n和m。
接下来m行,每行包含两个整数a和b,表示存在一条从a走到b的长度为1的边。
        
          输出格式
      输出一个整数,表示1号点到n号点的最短距离。
        
          数据范围
      1≤n,m≤10^5
        
          输入样例:
      
        
          输出样例:
      
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
   | #include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<cstring> using namespace std; const int N=100050; int n,m; struct node {     int to,next; }e[N]; int head[N],d[N],vis[N]; int cnt=0; void add(int u,int v) {     e[++cnt].to=v;     e[cnt].next=head[u];     head[u]=cnt; } void bfs() {     queue<int> q;     q.push(1);     d[1]=0;     while(!q.empty())     {         int u=q.front();                  q.pop();         for(int i=head[u];i;i=e[i].next)         {             int to=e[i].to;             if(d[to]==-1)             {                 d[to]=d[u]+1;                 q.push(to);             }         }     } } int main() {     ios::sync_with_stdio(0);     cin>>n>>m;     for(int i=1;i<=m;i++)     {         int a, b;         cin>>a>>b;         add(a,b);     }     memset(d,-1,sizeof(d));     bfs();     cout<<d[n];     return 0;  }
   | 
 
        
          拓扑序
      一些概念:
拓扑序是针对有向图而言的。
入度:有几条边指向这个点(入边);     出度:有几条出边。
如果图上有环,则不存在拓扑序。因此有向无环图才存在拓扑序,有向无环图(DAG)又称为拓扑图。一个有向无环图一定至少存在一个入度为0的点。
        
          如何求拓扑序:
      基于宽搜
- 在拓扑图中,所有入度为0的点都可以作为起点。因此第一步就是先将所有入度为0的点入队。
 
- 循环:枚举队头的所有出边,u–>v,删掉u–>v的这条边,即d[v]–;
 
- 如果d[v]==0,就可以入队。完成整个过程就找到了拓扑序。
 
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
   | #include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<cstring> using namespace std; const int N=100050; int n,m; struct node {     int to,next; }e[N]; int head[N],d[N],vis[N]; int ans[N]; int cnt=0; int tot=0; void add(int u,int v) {     e[++cnt].to=v;     e[cnt].next=head[u];     head[u]=cnt; } int bfs() {     queue<int> q;      for(int i=1;i<=n;i++)     {         if(d[i]==0)q.push(i);     }     if(q.empty())return 0;     while(!q.empty())     {         int u=q.front();         ans[++tot]=u;         q.pop();         for(int i=head[u];i;i=e[i].next)         {             int to=e[i].to;             d[to]--;             if(d[to]==0)q.push(to);         }     }     if(tot==n)return 1;     else return 0; } int main() {     ios::sync_with_stdio(0);     cin>>n>>m;     for(int i=1;i<=m;i++)     {         int a,b;         cin>>a>>b;         add(a,b);         d[b]++;     }     if(bfs())     {         for(int i=1;i<=n;i++)         {             cout<<ans[i]<<" ";         }     }     else      {         cout<<-1;     }     return 0;  }
   |