与树的遍历类似,图的遍历就是从图的某一点出发,按照某搜索方式对图中的所有节点都仅访问一次。图的遍历根据搜索方式不同,分为广度优先遍历和深度优先遍历。

广度优先遍历(Breadth First Search,BFS)

广度遍历也就是宽度优先遍历,指从某个点出发,一次性访问所有未被访问的邻接点,再依次从这些已经访问过的邻接点出发,一层一层的访问。他的特点就是先被访问的节点,其邻接点先被访问。
由他的特点可以想到,先来先搜索,显然可借助队列特性。又因为每个节点只访问一次,所以设置一个辅助数组visit【】,初值为FALSE,访问后该节点标为TRUE。
算法步骤

  1. 初始化所有节点均未被访问,并初始化一个空队列。
  2. 从图中某个节点v出发,访问v并标记为已被访问,将v入队。
  3. 若队列非空,则继续执行,否则算法结束。
  4. 将队头元素v出队,依次访问v的所有未被访问的邻接点,标记已被访问并入队,转向步骤3.

图解见视频。
广度优先遍历经过的节点和边,被称作广度优先生成树,若广度优先遍历非连通图,则每个连通分量都会产生一棵广度优先生成树。
算法实现:

基于邻接矩阵实现的广度优先遍历

  1. #include<iostream>
  2. #include<queue>//引入队列头文件
  3. using namespace std;
  4. const int MaxVnum=100;//顶点数最大值
  5. bool visited[MaxVnum]; //访问标志数组,其初值为"false"
  6. typedef char VexType; //顶点的数据类型,根据需要定义
  7. typedef int EdgeType; //边上权值的数据类型,若不带权值的图,则为0或1
  8. typedef struct{
  9. VexType Vex[MaxVnum];
  10. EdgeType Edge[MaxVnum][MaxVnum];
  11. int vexnum,edgenum; //顶点数,边数
  12. }AMGraph;
  13. int locatevex(AMGraph G,VexType x){
  14. for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
  15. if(x==G.Vex[i])
  16. return i;
  17. return -1;//没找到
  18. }
  19. void CreateAMGraph(AMGraph &G){//创建有向图的邻接矩阵
  20. int i,j;
  21. VexType u,v;
  22. cout<<"请输入顶点数:"<<endl;
  23. cin>>G.vexnum;
  24. cout<<"请输入边数:"<<endl;
  25. cin>>G.edgenum;
  26. cout<<"请输入顶点信息:"<<endl;
  27. for(int i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
  28. cin>>G.Vex[i];
  29. for(int i=0;i<G.vexnum;i++)//初始化邻接矩阵所有值为0,如果是网,则初始化邻接矩阵为无穷大
  30. for(int j=0;j<G.vexnum;j++)
  31. G.Edge[i][j]=0;
  32. cout<<"请输入每条边依附的两个顶点:"<<endl;
  33. while(G.edgenum--){
  34. cin>>u>>v;
  35. i=locatevex(G,u);//查找顶点u的存储下标
  36. j=locatevex(G,v);//查找顶点v的存储下标
  37. if(i!=-1&&j!=-1)
  38. G.Edge[i][j]=1; //邻接矩阵储置1,若无向图G.Edge[i][j]=G.Edge[j][i]=1
  39. else{
  40. cout<<"输入顶点信息错!请重新输入!"<<endl;
  41. G.edgenum++;//本次输入不算
  42. }
  43. }
  44. }
  45. void print(AMGraph G){//输出邻接矩阵
  46. cout<<"图的邻接矩阵为:"<<endl;
  47. for(int i=0;i<G.vexnum;i++){
  48. for(int j=0;j<G.vexnum;j++)
  49. cout<<G.Edge[i][j]<<"\t";
  50. cout<<endl;
  51. }
  52. }
  53. void BFS_AM(AMGraph G,int v){//基于邻接矩阵的广度优先遍历
  54. int u,w;
  55. queue<int>Q; //创建一个普通队列(先进先出),里面存放int类型
  56. cout<<G.Vex[v]<<"\t";
  57. visited[v]=true;
  58. Q.push(v); //源点v入队
  59. while(!Q.empty()){ //如果队列不空
  60. u=Q.front();//取出队头元素赋值给u
  61. Q.pop(); //队头元素出队
  62. for(w=0;w<G.vexnum;w++){//依次检查u的所有邻接点
  63. if(G.Edge[u][w]&&!visited[w]){//u、w邻接而且w未被访问
  64. cout<<G.Vex[w]<<"\t";
  65. visited[w]=true;
  66. Q.push(w);
  67. }
  68. }
  69. }
  70. }
  71. int main(){
  72. int v;
  73. VexType c;
  74. AMGraph G;
  75. CreateAMGraph(G);
  76. print(G);
  77. cout << "请输入遍历图的起始点:";
  78. cin>>c;
  79. v=locatevex(G,c);//查找顶点u的存储下标
  80. if(v!=-1){
  81. cout<<"广度优先搜索遍历图结果:"<<endl;
  82. BFS_AM(G,v);
  83. }
  84. else
  85. cout<<"输入顶点信息错!请重新输入!"<<endl;
  86. return 0;
  87. }
  88. /*测试数据
  89. 6 9
  90. 1 2 3 4 5 6
  91. 1 3
  92. 1 2
  93. 2 4
  94. 3 5
  95. 3 2
  96. 4 6
  97. 4 3
  98. 5 6
  99. 5 4
  100. 1
  101. */

基于邻接表的广度优先遍历

  1. #include<iostream>
  2. #include<queue>//引入队列头文件
  3. using namespace std;
  4. const int MaxVnum=100;//顶点数最大值
  5. bool visited[MaxVnum]; //访问标志数组,其初值为"false"
  6. typedef char VexType;//顶点的数据类型为字符型
  7. typedef struct AdjNode{ //定义邻接点类型
  8. int v; //邻接点下标
  9. struct AdjNode *next; //指向下一个邻接点
  10. }AdjNode;
  11. typedef struct VexNode{ //定义顶点类型
  12. VexType data; // VexType为顶点的数据类型,根据需要定义
  13. AdjNode *first; //指向第一个邻接点
  14. }VexNode;
  15. typedef struct{//定义邻接表类型
  16. VexNode Vex[MaxVnum];
  17. int vexnum,edgenum; //顶点数,边数
  18. }ALGraph;
  19. int locatevex(ALGraph G,VexType x){
  20. for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
  21. if(x==G.Vex[i].data)
  22. return i;
  23. return -1;//没找到
  24. }
  25. void insertedge(ALGraph &G,int i,int j){//插入一条边
  26. AdjNode *s;
  27. s=new AdjNode;
  28. s->v=j;
  29. s->next=G.Vex[i].first;
  30. G.Vex[i].first=s;
  31. }
  32. void printg(ALGraph G){//输出邻接表
  33. cout<<"----------邻接表如下:----------"<<endl;
  34. for(int i=0;i<G.vexnum;i++){
  35. AdjNode *t=G.Vex[i].first;
  36. cout<<G.Vex[i].data<<": ";
  37. while(t!=NULL){
  38. cout<<"["<<t->v<<"] ";
  39. t=t->next;
  40. }
  41. cout<<endl;
  42. }
  43. }
  44. void CreateALGraph(ALGraph &G){//创建有向图邻接表
  45. int i,j;
  46. VexType u,v;
  47. cout<<"请输入顶点数和边数:"<<endl;
  48. cin>>G.vexnum>>G.edgenum;
  49. cout<<"请输入顶点信息:"<<endl;
  50. for(i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
  51. cin>>G.Vex[i].data;
  52. for(i=0;i<G.vexnum;i++)
  53. G.Vex[i].first=NULL;
  54. cout<<"请依次输入每条边的两个顶点u,v"<<endl;
  55. while(G.edgenum--){
  56. cin>>u>>v;
  57. i=locatevex(G,u);//查找顶点u的存储下标
  58. j=locatevex(G,v);//查找顶点v的存储下标
  59. if(i!=-1&&j!=-1)
  60. insertedge(G,i,j);
  61. else{
  62. cout<<"输入顶点信息错!请重新输入!"<<endl;
  63. G.edgenum++;//本次输入不算
  64. }
  65. }
  66. }
  67. void BFS_AL(ALGraph G,int v){//基于邻接表的广度优先遍历
  68. int u,w;
  69. AdjNode *p;
  70. queue<int>Q; //创建一个普通队列(先进先出),里面存放int类型
  71. cout<<G.Vex[v].data<<"\t";
  72. visited[v]=true;
  73. Q.push(v); //源点v入队
  74. while(!Q.empty()){ //如果队列不空
  75. u=Q.front();//取出队头元素赋值给u
  76. Q.pop(); //队头元素出队
  77. p=G.Vex[u].first;
  78. while(p){//依次检查u的所有邻接点
  79. w=p->v;//w为u的邻接点
  80. if(!visited[w]){//w未被访问
  81. cout<<G.Vex[w].data<<"\t";
  82. visited[w]=true;
  83. Q.push(w);
  84. }
  85. p=p->next;
  86. }
  87. }
  88. }
  89. void BFS_AL(ALGraph G){//非连通图,基于邻接表的广度优先遍历
  90. for(int i=0;i<G.vexnum;i++)//非连通图需要查漏点,检查未被访问的顶点
  91. if(!visited[i])//i未被访问,以i为起点再次广度优先遍历
  92. BFS_AL(G,i);
  93. }
  94. int main(){
  95. ALGraph G;
  96. int v;
  97. VexType c;
  98. CreateALGraph(G);//创建有向图邻接表
  99. printg(G);//输出邻接表
  100. cout<<"请输入遍历图的起始点:";
  101. cin>>c;
  102. v=locatevex(G,c);//查找顶点u的存储下标
  103. if(v!=-1){
  104. cout<<"广度优先搜索遍历图结果:"<<endl;
  105. BFS_AL(G,v);
  106. }
  107. else
  108. cout<<"输入顶点信息错!请重新输入!"<<endl;
  109. return 0;
  110. }
  111. /*测试数据
  112. 6 9
  113. 1 2 3 4 5 6
  114. 1 3
  115. 1 2
  116. 2 4
  117. 3 5
  118. 3 2
  119. 4 6
  120. 4 3
  121. 5 6
  122. 5 4
  123. 1
  124. */

深度优先遍历(Depth First Search,DFS)

深度优先搜索,是指沿着一条路径一直搜索下去,在无法搜索时,回退到刚刚访问的节点。深度优先遍历是按照深度优先搜索的方式对图进行遍历的。它的特点就是:后被访问的节点,其邻接点先被访问到。
显然,根据后来者先被搜索的特性,我们需要栈来辅助我们实现。递归本来就是使用栈实现的,因而使用递归的方法更方便。
算法步骤

  1. 初始化图中的所有节点均未被访问
  2. 从图中的某个节点v出发,访问v并标记
  3. 依次检查v的邻接点w,若w未被访问,则从w出发进行深度优先遍历(递归调用,重复2,3步)

图解如视频.

基于邻接矩阵实现的深度优先遍历

#include<iostream>
using namespace std;
const int MaxVnum=100;     //顶点数最大值
bool visited[MaxVnum];  //访问标志数组,其初值为"false"
typedef char VexType;  //顶点的数据类型,根据需要定义
typedef int EdgeType;  //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct{
    VexType Vex[MaxVnum];
    EdgeType Edge[MaxVnum][MaxVnum];
    int vexnum,edgenum; //顶点数,边数
}AMGraph;

int locatevex(AMGraph G,VexType x){
    for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
        if(x==G.Vex[i])
            return i;
    return -1;//没找到
}

void CreateAMGraph(AMGraph &G){//创建无向图的邻接矩阵
    int i,j;
    VexType u,v;
    cout<<"请输入顶点数:"<<endl;
    cin>>G.vexnum;
    cout<<"请输入边数:"<<endl;
    cin>>G.edgenum;
    cout<<"请输入顶点信息:"<<endl;
    for(int i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
        cin>>G.Vex[i];
    for(int i=0;i<G.vexnum;i++)//初始化邻接矩阵所有值为0,如果是网,则初始化邻接矩阵为无穷大
        for(int j=0;j<G.vexnum;j++)
            G.Edge[i][j]=0;
    cout<<"请输入每条边依附的两个顶点:"<<endl;
    while(G.edgenum--){
        cin>>u>>v;
        i=locatevex(G,u);//查找顶点u的存储下标
        j=locatevex(G,v);//查找顶点v的存储下标
        if(i!=-1&&j!=-1)
            G.Edge[i][j]=G.Edge[j][i]=1; //若有向图G.Edge[i][j]=1
        else{
            cout<<"输入顶点信息错!请重新输入!"<<endl;
            G.edgenum++;//本次输入不算
        }
    }
}

void print(AMGraph G){//输出邻接矩阵
    cout<<"图的邻接矩阵为:"<<endl;
    for(int i=0;i<G.vexnum;i++){
        for(int j=0;j<G.vexnum;j++)
            cout<<G.Edge[i][j]<<"\t";
        cout<<endl;
    }
}

void DFS_AM(AMGraph G,int v){//基于邻接矩阵的深度优先遍历
    int w;
    cout<<G.Vex[v]<<"\t";
    visited[v]=true;
    for(w=0;w<G.vexnum;w++)//依次检查v的所有邻接点
        if(G.Edge[v][w]&&!visited[w])//v、w邻接而且w未被访问
            DFS_AM(G,w);//从w顶点开始递归深度优先遍历
}

int main(){
    int v;
    VexType c;
    AMGraph G;
    CreateAMGraph(G);//创建无向图的邻接矩阵 
    print(G);
    cout<<"请输入遍历连通图的起始点:";
    cin>>c;
    v=locatevex(G,c);//查找顶点u的存储下标
    if(v!=-1){
        cout<<"深度优先搜索遍历连通图结果:"<<endl;
        DFS_AM(G,v);
    }
    else
        cout<<"输入顶点信息错!请重新输入!"<<endl;
    return 0;
}
/*测试数据 
8 9
1 2 3 4 5 6 7 8
1 3
1 2
2 6
2 5
2 4
3 8
3 7
4 5
7 8
1
*/

基于邻接表实现的深度优先遍历

#include<iostream>
using namespace std;
const int MaxVnum=100;  //顶点数最大值
bool visited[MaxVnum];  //访问标志数组,其初值为"false"
typedef char VexType;   //顶点的数据类型为字符型
typedef struct AdjNode{ //定义邻接点类型
    int v; //邻接点下标
    struct AdjNode *next; //指向下一个邻接点
}AdjNode;

typedef struct VexNode{ //定义顶点类型
   VexType data; // VexType为顶点的数据类型,根据需要定义
   AdjNode *first; //指向第一个邻接点
}VexNode;

typedef struct{//定义邻接表类型
    VexNode  Vex[MaxVnum];
    int vexnum,edgenum; //顶点数,边数
}ALGraph;

int locatevex(ALGraph G,VexType x){
    for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
        if(x==G.Vex[i].data)
            return i;
    return -1;//没找到
}

void insertedge(ALGraph &G,int i,int j){//插入一条边
    AdjNode *s;
    s=new AdjNode;
    s->v=j;
    s->next=G.Vex[i].first;
    G.Vex[i].first=s;
}

void printg(ALGraph G){//输出邻接表
    cout<<"----------邻接表如下:----------"<<endl;
    for(int i=0;i<G.vexnum;i++){
        AdjNode *t=G.Vex[i].first;
        cout<<G.Vex[i].data<<":  ";
        while(t!=NULL){
           cout<<"["<<t->v<<"]  ";
           t=t->next;
        }
        cout<<endl;
    }
}

void CreateALGraph(ALGraph &G){//创建无向图邻接表
    int i,j;
    VexType u,v;
    cout<<"请输入顶点数和边数:"<<endl;
    cin>>G.vexnum>>G.edgenum;
    cout<<"请输入顶点信息:"<<endl;
    for(i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
        cin>>G.Vex[i].data;
    for(i=0;i<G.vexnum;i++)
        G.Vex[i].first=NULL;
    cout<<"请依次输入每条边的两个顶点u,v"<<endl;
    while(G.edgenum--){
        cin>>u>>v;
        i=locatevex(G,u);//查找顶点u的存储下标
        j=locatevex(G,v);//查找顶点v的存储下标
        if(i!=-1&&j!=-1){
            insertedge(G,i,j);
            insertedge(G,j,i);//无向图多插入一条边
        }
        else{
            cout<<"输入顶点信息错!请重新输入!"<<endl;
            G.edgenum++;//本次输入不算
        }
    }
}

void DFS_AL(ALGraph G,int v){//基于邻接表的深度优先遍历
    int w;
    AdjNode *p;
    cout<<G.Vex[v].data<<"\t";
    visited[v]=true;
    p=G.Vex[v].first;
    while(p){//依次检查v的所有邻接点
        w=p->v;//w为v的邻接点
        if(!visited[w])//w未被访问
               DFS_AL(G,w);//从w出发,递归深度优先遍历
        p=p->next;
    }
}

void DFS_AL(ALGraph G){//非连通图,基于邻接表的深度优先遍历
    for(int i=0;i<G.vexnum;i++)//非连通图需要查漏点,检查未被访问的顶点
        if(!visited[i])//i未被访问,以i为起点再次深度优先遍历
               DFS_AL(G,i);
}

int main(){
    ALGraph G;
    int v;
    VexType c;
    CreateALGraph(G);//创建无向图的邻接表
    printg(G);//输出邻接表
    cout<<"请输入遍历连通图的起始点:";
    cin>>c;
    v=locatevex(G,c);//查找顶点u的存储下标
    if(v!=-1){
        cout<<"深度优先搜索遍历连通图结果:"<<endl;
        DFS_AL(G,v);
    }
    else
        cout<<"输入顶点信息错!请重新输入!"<<endl;
    return 0;
}
/*测试数据 
8 9
1 2 3 4 5 6 7 8
1 3
1 2
2 6
2 5
2 4
3 8
3 7
4 5
7 8
1
*/

真题练习

题目描述(蓝桥2017省赛填空题)

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
X 星球的一处迷宫游乐场建在某个小山坡上。它是由 10×1010 \times 1010×10 相互连通的小房间组成的。
房间的地板上写着一个很大的字母。我们假设玩家是面朝上坡的方向站立,则:

  • LLL 表示走到左边的房间,
  • RRR 表示走到右边的房间,
  • UUU 表示走到上坡方向的房间,
  • DDD 表示走到下坡方向的房间。

X 星球的居民有点懒,不愿意费力思考。他们更喜欢玩运气类的游戏。这个游戏也是如此!
开始的时候,直升机把 100100100 名玩家放入一个个小房间内。玩家一定要按照地上的字母移动。
迷宫地图如下:
UDDLUULRUL
UURLLLRRRU
RRUURLDLRD
RUDDDDUUUU
URUDLLRRUU
DURLRLDLRL
ULLURLLRDU
RDLULLRDDD
UUDDUDUDLL
ULRDLUURRR

请你计算一下,最后,有多少玩家会走出迷宫,而不是在里边兜圈子?
如果你还没明白游戏规则,可以参看下面一个简化的 4x4 迷宫的解说图:
bfs和dfs - 图1

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 128M ```cpp

    include

    using namespace std;

int cnt = 0; int vis[11][11]; char a[11][11];

void dfs(int x,int y) { if(x<1 || y<1 || x>10 || y>10){ cnt++; return ; } if(vis[x][y]) return ; vis[x][y] = true; if(a[x][y] == ‘U’) dfs(x-1,y); if(a[x][y] == ‘D’) dfs(x+1,y); if(a[x][y] == ‘L’) dfs(x,y-1); if(a[x][y] == ‘R’) dfs(x,y+1); }

int main() { fstream f1(“a.txt”); for(int i=1;i<=10;i++) { for(int j=1;j<=10;j++) { f1>>a[i][j]; } } for(int i=1;i<=10;i++) { for(int j=1;j<=10;j++) { for(int t=1;t<=10;t++) { for(int k=1;k<=10;k++) { vis[t][k] = false; } } dfs(i,j); } } cout<<cnt<<endl; return 0; }


<a name="Z44rs"></a>
# 蓝桥杯真题:全球变暖(2018 年省赛) 
<a name="fuDyN"></a>
### 题目描述
你有一张某海域 NxNNxNNxN 像素的照片,"."表示海洋、"#"表示陆地,如下所示:<br />.......<br />.##....<br />.##....<br />....##.<br />..####.<br />...###.<br />.......<br />其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 2 座岛屿。<br />由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。<br />例如上图中的海域未来会变成如下样子:<br />.......<br />.......<br />.......<br />.......<br />....#..<br />.......<br />.......<br />请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
<a name="ZvfJd"></a>
### 输入描述
第一行包含一个整数 N (1≤N≤1000)N\ (1 \leq N \leq 1000)N (1≤N≤1000)。<br />以下 NNN 行 NNN 列代表一张海域照片。<br />照片保证第 1 行、第 1 列、第 NNN 行、第 NNN 列的像素都是海洋。、<br />输出一个整数表示答案。
<a name="lJn4J"></a>
### 输入输出样例
<a name="YzjFr"></a>
#### 示例
输入<br />7 ....... .##.... .##.... ....##. ..####. ...###. ....... <br />输出<br />1 
<a name="lV4Xi"></a>
### 运行限制

- 最大运行时间:1s 
- 最大运行内存: 256M
```cpp
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;

const  int maxn = 1005;
char a[maxn][maxn];
int n;
int vis[maxn][maxn];
int dir[4][2]={{-1,0},{0,-1},{0,1},{1,0}};

struct point
{
    int x,y;
    point(int _a,int _b)
    {
        x = _a;
        y = _b;
    }
};

int bfs(int x,int y)
{
    queue<point> q;
    q.push(point(x,y));
    int left = 0;
    while(!q.empty())
    {
        point p = q.front();
        q.pop();
        int cnt = 0;
        for(int i = 0;i<4;i++)
        {
            int dx = p.x+dir[i][0],dy = p.y+dir[i][1];
            if(dx < 0||dx >= n||dy < 0||dy >= n)
                continue;
            if(a[dx][dy] == '#')
            {
                cnt++;
                if(!vis[dx][dy])
                {
                    vis[dx][dy]=1;
                    q.push(point(dx,dy));
                }
            }
        }
        if(cnt == 4)
            left++;
    }
    return left;
}

int main()
{
    int ans = 0;
    scanf("%d", &n);
    getchar();
    for(int i = 0;i < n;i++)
    {
            scanf("%s", a[i]);
    }

    memset(vis, 0, sizeof(vis));

    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < n; j++)
        {
            if(a[i][j] == '#' && !vis[i][j])
            {
                vis[i][j]=1;
                if(!bfs(i,j))
                    ans++;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

题目描述(蓝桥2019 省赛填空)

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可以通行的地方。
010000 000100 001001 110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这 个它的上、下、左、右四个方向之一。
对于上面的迷宫,从入口开始,可以按 DRRURRDDDR 的顺序通过迷宫, 一共 10 步。其中 D、U、L、R分别表示向下、向上、向左、向右走。 对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式,其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。
请注意在字典序中 D01010101001011001001010110010110100100001000101010 00001000100000101010010000100000001001100110100101 01111011010010001000001101001011100011000000010000 01000000001010100011010000101000001010101011001011 00011111000000101000010010100010100000101100000000 11001000110101000010101100011010011010101011110111 00011011010101001001001010000001000101001110000000 10100000101000100110101010111110011000010000111010 00111000001010100001100010000001000101001100001001 11000110100001110010001001010101010101010001101000 00010000100100000101001010101110100010101010000101 11100100101001001000010000010101010100100100010100 00000010000000101011001111010001100000101010100011 10101010011100001000011000010110011110110100001000 10101010100001101010100101000010100000111011101001 10000000101100010000101100101101001011100000000100 10101001000000010100100001000100000100011110101001 00101001010101101001010100011010101101110000110101 11001010000100001100000010100101000001000111000010 00001000110000110101101000000100101001001000011101 10100101000101000000001110110010110101101010100001 00101000010000110101010000100010001001000100010101 10100001000110010001000010101001010101011111010010 00000100101000000110010100101001000001000000000010 11010000001001110111001001000011101001011011101000 00000110100010001000100000001000011101000000110011 10101000101000100010001111100010101001010000001000 10000010100101001010110000000100101010001011101000 00111100001000010000000110111000000001000000001011 10000001100111010111010001000110111010101101111000

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M ```cpp

    include

    include

    include

    using namespace std; int arr[32][52] = { 0 }; int dir[4][2] = { 1,0,0,-1,0,1,-1,0 }; char ch[4] = { ‘D’,’L’,’R’,’U’ }; int dis[32][52]; bool mark[32][52] = { 0 }; void BFS(int x,int y,int z) { for (int i = 0; i < 4; i++) {
      if (arr[x + dir[i][0]][y + dir[i][1]] == 0 && mark[x + dir[i][0]][y + dir[i][1]] == 0)
      {
          mark[x + dir[i][0]][y + dir[i][1]] = 1;
          dis[x + dir[i][0]][y + dir[i][1]] = z + 1;
          BFS(x + dir[i][0], y + dir[i][1], z + 1);
      }
      if (arr[x + dir[i][0]][y + dir[i][1]] == 0 && dis[x + dir[i][0]][y + dir[i][1]] > z+1)
      {
          mark[x + dir[i][0]][y + dir[i][1]] = 1;
          dis[x + dir[i][0]][y + dir[i][1]] = z + 1;
          BFS(x + dir[i][0], y + dir[i][1], z + 1);
      }
    
    } } bool flag = false; vector ve; void output() { for (int i = 0; i < ve.size(); i++) {
      cout << ve[i];
    
    } cout << endl; } void DFS(int x,int y,int z) { if (x == 30 && y == 50) {
      output();
      flag = true; 
    
    } if (flag)return; for (int i = 0; i < 4; i++) {
      if (dis[x + dir[i][0]][y + dir[i][1]] == z+1)
      {
          ve.push_back(ch[i]);
          DFS(x + dir[i][0], y + dir[i][1], z + 1);
          ve.pop_back();
      }
    
    } } int main() { memset(dis, -1, sizeof(dis)); for (int i = 0; i < 32; i++) {
      arr[i][0] = 1;
      mark[i][0] = 1;
      arr[i][51] = 1;
      mark[i][51] = 1;
    
    } for (int i = 0; i < 52; i++) {
      arr[0][i] = 1;
      mark[0][i] = 1;
      arr[31][i] = 1;
      mark[31][i] = 1;
    
    } for (int i = 1; i < 31; i++) {
      for (int j = 1; j < 51; j++)
      {
          arr[i][j] = getchar()-'0';
          int r = arr[i][j];
          if (arr[i][j] == 1)mark[i][j] = 1;
      }
      char h=getchar();
    
    } dis[1][1] = 0; BFS(1, 1, 0); DFS(1,1,0); return 0; }
```cpp
#include <iostream>
using namespace std;
int main()
{
  cout<<"DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR"<<endl;// 请在此输入您的代码
  return 0;
}

题目背景(冬季赛真题)

树的果实
Description
小Y同学是一个高中信息学奥赛里面的蒟蒻,在各位大佬暴切各种dark火题的时候,他还在瑟瑟发抖的学习各种基础知识,在巩固很久的基础知识之后,他终于尝试学习且粗略学懂了他之前一直觉得很高大上的树链剖分,于是他准备尝试去写一写模板题。他点开了做题网站,看到了讨论版一句“萌新学妹刚学oi一个月,树链剖分打错了,求大佬看看”,于是小Y同学立即正义感爆棚点进了这一道题。

题目描述

你梦见了一棵树,这是一棵很茂密的树,因此它有很多的分支。
你注意到这颗树的有 n 个果实,每一棵果实都有自己的编号,且标号为 1 的果实在最上面,像是一个根节点,树上的一个果实 u 到另一个果实 v 的距离,都恰好是一个整数 c ,因为已经固定好了 1 号果实为根节点,所以这棵树的形状已经确定了,你想知道摘下一颗果实,会连带着把它的子树的果实也给摘下来。
而这个摘下来所得到的贡献为(数字出现的次数*数字)的平方
比如2出现了5次,那么贡献即为(25)^2
*数字为两个果实之间的距离即树的边权值,边权值的范围为 c 。

所以你有m组询问,想知道当前询问的果实连带着它的子树果实被摘下来时的贡献是多少。
Input
第1行,三个整数n,m,c分别表示树的大小,询问的个数,边权的范围。(1≤n,m,c≤100000)(1 \leq n,m,c \leq 100000)(1≤n,m,c≤100000)
第2−n行,每行三个整数u,v,vi表示从u到v有一条vi边权的边。
接下来m行,每行一个整数表示询问的节点。
Output
输出m行,每行一个整数代表子树的权值大小。(保证不会超过long long)

Sample Input 1
11 6 10 1 2 9 2 3 1 3 4 6 2 5 7 4 6 5 5 7 7 7 8 8 7 9 3 7 10 6 3 11 3 5 7 10 6 1 5
Sample Output 1
158 109 0 0 547 158
Hint
bfs和dfs - 图2
样例如图
对于5节点的子树,贡献为(71)2+(81)2+(31)2+(61)2=158(71)^2+(81)^2+(31)^2+(61)^2=158(71)2+(81)2+(31)2+(61)2=158
对于7节点的子树,贡献为(81)2+(31)2+(6∗1)2=109(81)^2+(31)^2+(61)^2=109(81)2+(31)2+(6∗1)2=109
对于1节点的子树贡献为(32)2+(62)2+(72)2+(51)2+(81)2+(91)2+(1∗1)2=547(32)^2+(62)^2+(72)^2+(51)^2+(81)^2+(91)^2+(1
1)^2=547(32)2+(62)2+(72)2+(51)2+(81)2+(91)2+(1∗1)2=547
对于6和10节点的子树内没有边,故没有边权做贡献,故贡献为000

#include<bits/stdc++.h>
using namespace std;
#define fo(a,b) for(int i=a;i<=b;i++)
#define ll long long
#define int long long
#define M 400010
#define ll long long
int n,m,k,s=0,v,i,j;
int res=0;
struct Node
{
    int id,l,r;
    bool operator<(const Node temp)const{
        if(l/v==temp.l/v) return r<temp.r;
        return l<temp.l;
    }
}x[M];
struct node
{
    vector<pair<int,int>>v;
}xx[M];
int a[M],ans[M],vis[M];
int in[M],out[M],cnt=0;
void dfs(int d,vector<pair<int,int>>&v,int pre)
{
    int l=v.size();
    in[d]=++cnt;
    a[cnt]=v[i].second;
    for(int i=0;i<l;i++){
        if(v[i].first!=pre){
            dfs(v[i].first,xx[v[i].first].v,d);
        }
    }
    out[d]=cnt;
}
void add(int d){
    vis[d]++;
    res+=d*d*(vis[d]*vis[d]-(vis[d]-1)*(vis[d]-1));
}
void del(int d){
    res+=d*d*(-vis[d]*vis[d]+(vis[d]-1)*(vis[d]-1));
    vis[d]--;
}
int solve(int l,int r){
    while(i<l) del(a[i++]);
    while(i>l) add(a[--i]);
    while(j<r) add(a[++j]);
    while(j>r) del(a[j--]);
    return res;
}
signed main()
{
    cin>>n>>m>>k;
    v=sqrt(n*1.0);
    for(int a,b,c,i=1;i<n;i++){
        scanf("%lld%lld%lld",&a,&b,&c);
        xx[a].v.push_back(make_pair(b,c));
        xx[b].v.push_back(make_pair(a,c));
    }
    dfs(1,xx[1].v,0);
    for(int i=1;i<=m;i++){
        int temp;
        scanf("%lld",&temp);
        x[i].l=in[temp]+1;
        x[i].r=out[temp];
        x[i].id=i;
    }
    sort(x+1,x+m+1);
    i=1,j=0,res=0;
    for(int i=1;i<=m;i++) ans[x[i].id]=solve(x[i].l,x[i].r);
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}