HNU数据结构与算法分析-实验五---图及图的相关算法

首先向大家介绍HNU数据结构与算法分析-实验体系

实验一共分为8个,

其中奇数个(1、3、5、7)为底层代码实现底层算法或数据结构,也称基础实验,

这一部分实验的代码需要同学们熟练掌握,甚至倒背如流,也会设置监考保证实验独立进行。

偶数个(2、4、6、8)为综合实验,是一些简单的应用类问题。

这八个实验合起来占HNUer的数据结构课程总成绩的20%。

最后,如果感觉有帮助的话还请点个赞,你的重要支持是我继续创作的动力。

那么让我们来看一下今天的题目吧
【原题】

【问题描述】

         在本问题,定义了图的两种表示方法:邻接表(链表)表示法及邻接矩阵表示法。

         头文件graph.h定义了图的ADT类Graph,头文件grlist.h中定义的类Graphl,实现了使用图的邻接表表示法,头文件grmat.h中定义的类Graphm,实现了图的邻接矩阵表示法,都是Graph类的子类,分别实现了Graph类中定义的所有纯虚方法。

        头文件Graph_test.h中的类option定义了图的相关算法。

        头文件link.h、list.h、llist.h定义了链表的相关操作。

        文件main.cpp为主函数源文件,其中包含了所有针对图的输入以及所有测试选项。

        graphAlg.cbp为基于Code::Blocks的项目文件。

        你需要下载压缩文件(下面将给出)

,创建一个文件夹,将该文件解压。无论你采用哪种编译器(Code::Blocks或Dev Cpp),都需要创建或打开项目,并将所有头文件以及源代码加入到项目之中。

        你的任务是:

你需要打开头文件grlist.h以及grmat.h,在函数getInDegree以及getOutDegree处填写相关代码,测试完成第1项任务。

你需要打开头文件Graph_test.h,填写相关代码,完成第2~5项任务。

除以上文件相应的位置外,请不要改变其他任何代码,否则将可能不能通过测试。

代码填写时,不必完成所有任务,任何任务都可以单独测试,建议你一项一项完成,提交代码时请将所有头文件以及源文件打包压缩提交。

测试用例有20个:

    1~4:用于测试入度/出度

    5~8:用于测试DFS算法

    9~12:用于测试BFS算法

    13~16:用于测试Dijkstra算法

    17~20:用于测试prim算法

    1. 求所有顶点的入度和出度

    2. 深度优先搜索(遍历)

    3. 广度优先搜索(遍历) 

    4. Dijkstra求最短路(从0点出发至其他各点)

    5. prim算法求最小支撑树(从0点出发至其他各点)

【输入形式】

       输入的第一行为一个整数choice,表示需要测试(执行)的操作或算法

         1.求所有顶点的入度和出度 

         2.深度优先搜索(遍历)

         3.广度优先搜索(遍历) 

         4.Dijkstra求最短路(从0点出发至其他各点)

         5.prim算法求最小支撑树(从0点出发至其他各点)

       输入的第二行为一个整数vert_num,表示树的节点数,编号从0~vert_num-1

       输入的第三行为一个整数graph_type,表示用哪种方法存储图:

         0.用邻接矩阵表示法

         1.用邻接表表示法

       输入的第四行为一个字符graph_dir:

         U.无向图

         D.有向图

       接下来的若干行,每行三个参数,表示图的边,第一个参数为出点,第二个参数为入点,第三个参数为边长(权重)

【注意】输入结束务必使用:“回车 Ctrl+Z 回车”的方式

【输出形式】

       根据第一个参数choice的选择不同,输出对应不同的结果,具体请参照样例输出。

【样例输入1】

1
6               
1
U               
0 4 9           
0 2 7
2 3 1
2 1 5
2 5 2
1 5 6
3 5 2

【样例输出1】

2 2 4 2 1 3
2 2 4 2 1 3

【样例输入2】

2
6               
1
U               
0 4 9           
0 2 7
2 3 1
2 1 5
2 5 2
1 5 6
3 5 2

【样例输出2】

PreVisit vertex 0
Visiting vertex 0
PreVisit vertex 2
Visiting vertex 2
PreVisit vertex 1
Visiting vertex 1
PreVisit vertex 5
Visiting vertex 5
PreVisit vertex 3
Visiting vertex 3
PostVisit vertex 3
PostVisit vertex 5
PostVisit vertex 1
PostVisit vertex 2
PreVisit vertex 4
Visiting vertex 4
PostVisit vertex 4
PostVisit vertex 0

【样例输入3】

3
6               
1
U               
0 4 9           
0 2 7
2 3 1
2 1 5
2 5 2
1 5 6
3 5 2

【样例输出3】

PreVisit vertex 0
Visiting vertex 0
PreVisit vertex 2
PreVisit vertex 4
PostVisit vertex 0
Visiting vertex 2
PreVisit vertex 1
PreVisit vertex 3
PreVisit vertex 5
PostVisit vertex 2
Visiting vertex 4
PostVisit vertex 4
Visiting vertex 1
PostVisit vertex 1
Visiting vertex 3
PostVisit vertex 3
Visiting vertex 5
PostVisit vertex 5

【样例输入4】

4
6               
1
U               
0 4 9           
0 2 7
2 3 1
2 1 5
2 5 2
1 5 6
3 5 2

【样例输出4】

0 12 7 8 9 9

【样例输入5】

5
6               
1
U               
0 4 9           
0 2 7
2 3 1
2 1 5
2 5 2
1 5 6
3 5 2

【样例输出5】

Add edge 0 to 2
Add edge 2 to 3
Add edge 2 to 5
Add edge 2 to 1
Add edge 0 to 4
0 5 1 1 9 2

 下面将给出源文件以及完整代码

1.main.cpp(主函数,作测试作用)

#include "grlist.h"
#include "grmat.h"
#include "Graph_test.h"
Graph *createGraph(int graph_type, int vert_num);

void PreVisit(int v)
{
    cout << "PreVisit vertex " << v << "\n";
}

void PostVisit(int v)
{
    cout << "PostVisit vertex " << v << "\n";
}

void Visiting(int v)
{
    cout << "Visiting vertex " << v << "\n";
}

int main()
{
    int choice;
    cin >> choice;                   // 选择操作

    Graph* G;
    int vert_num;                     // 图的顶点数,编号从0开始
    cin >> vert_num;

    int graph_type = 2;               // graph_type=1, 采用临接链表表示图   graph_type=0, 采用临接矩阵表示图
    while(graph_type != 0 && graph_type != 1)
        cin >> graph_type;

    G = createGraph(graph_type, vert_num);

    char graph_dir = '0';             // graph_dir='D'为有向图 graph_dir='U'为无向图

    while(graph_dir != 'D' && graph_dir != 'U')
        cin >> graph_dir;



    int fr_vert, to_vert, wt;
    while(cin >> fr_vert >> to_vert >> wt)
    {
        G->setEdge(fr_vert, to_vert, wt);
        if (graph_dir == 'U')
            G->setEdge(to_vert, fr_vert, wt);
    }

    option *it = new option(G);
    for (int v = 0; v < G->n(); v++)
        G->setMark(v, UNVISITED);  // Initialize mark bits

    int D[G->n()];

    switch(choice)
    {
    case 1:    //求所有节点的入度和出度
        for(int i = 0; i < G->n(); i++)
            cout << G->getInDegree(i) << " ";
        cout << endl;

        for(int i = 0; i < G->n(); i++)
            cout << G->getOutDegree(i) << " ";
        cout << endl;
        break;
    case 2:   // 深度优先搜索
        for (int v = 0; v < G->n(); v++)
        {
            if (G->getMark(v) == UNVISITED)
                it->DFS(v, PreVisit, PostVisit, Visiting);
        }
        break;
    case 3:    //广度优先搜索
        for (int v = 0; v < G->n(); v++)
        {
            if (G->getMark(v) == UNVISITED)
                it->BFS(v, PreVisit, PostVisit, Visiting);
        }
        break;

    case 4:   // Dijkstra求最短路(从0点出发至其他各点)

        for (int i = 0; i < G->n(); i++) // Initialize
            D[i] = INFINITY;
        D[0] = 0;

        it->Dijkstra1(D, 0);

        for(int k = 0; k < G->n(); k++)
        {

            if (D[k] != INFINITY)
                cout << D[k] << " ";
            else
                cout << "Infinity" << " ";
        }
        cout << endl;
        break;

    case 5:   // prim算法求最小支撑树(从0点出发至其他各点)

        for (int i = 0; i < G->n(); i++) // Initialize
            D[i] = INFINITY;
        D[0] = 0;

        it->Prim(D, 0);

        for(int k = 0; k < G->n(); k++)
            cout << D[k] << " ";
        cout << endl;
        break;
    }
    return 0;
}

Graph *createGraph(int graph_type, int vert_num)
{
    Graph *g;
    if (graph_type)
        g = new Graphl(vert_num);
    else
        g = new Graphm(vert_num);

    return g;
}

2.Graph_test.h(算法实现的文件,也是最重要的一部分)

#ifndef GRAPH_TEST_H_INCLUDED
#define GRAPH_TEST_H_INCLUDED
#define INFINITY 1000000
#include "grlist.h"
#include "grmat.h"
#include 

class option
{
private:
    Graph *G;
public:
// Start with some implementations for the abstract functions
    option(Graph *g)
    {
        G = g;
    }


    void DFS(int v, void (*PreVisit)(int v), void (*PostVisit)(int v), void (*Visiting)(int v))   // Depth first search
    {
    PreVisit(v);
	Visiting(v);
        G->setMark(v, VISITED);
        for (int w = G->first(v); w < G->n(); w = G->next(v, w))
            if (G->getMark(w) == UNVISITED)
                DFS(w, *PreVisit, *PostVisit, *Visiting);
    PostVisit(v);
    }
    
    void BFS(int start, void (*PreVisit)(int v), void (*PostVisit)(int v), void (*Visiting)(int v))
    {
     int v,w;   
	 queue q; 
        q.push(start);
        PreVisit(start);
        G->setMark(start, VISITED);
        while (q.size()!=0) {
            v = q.front();
            q.pop();
			Visiting(v); 
            for (w = G->first(v); w < G->n(); w = G->next(v, w))
                {
				if (G->getMark(w) == UNVISITED) { 
                    G->setMark(w,VISITED);
					PreVisit(w);
                    q.push(w);
                }	
			}
			PostVisit(v);			
        }
    }

    void Dijkstra1(int* D, int s)
    {
    int i, v, w;
        for (i = 0; i < G->n(); i++) {
            v = minVertex(D);
            if (D[v] == INFINITY) return;
            G->setMark(v, VISITED);
            for (w = G->first(v); w < G->n(); w = G->next(v, w))
                if (D[w] > (D[v] + G->weight(v, w)))
                    D[w] = D[v] + G->weight(v, w);
        }
    }

    int minVertex(int* D)   // Find min cost vertex
    {
        int i, v = -1;
        // Initialize v to some unvisited vertex
        for (i = 0; i < G->n(); i++)
            if (G->getMark(i) == UNVISITED)
            {
                v = i;
                break;
            }
        for (i++; i < G->n(); i++) // Now find smallest D value
            if ((G->getMark(i) == UNVISITED) && (D[i] < D[v]))
                v = i;
        return v;
    }

    void AddEdgetoMST(int v1, int v2)
    {
        cout << "Add edge " << v1 << " to " << v2 << "\n";
    }
    void Prim(int* D, int s)   // Prim's MST algorithm
    {
    int V [G->n()];                     // Store closest vertex 存储最近节点
        int i, w;
        for (i = 0; i < G->n(); i++) {         // Process the vertices 处理节点
            int v = minVertex(D);
            G->setMark(v, VISITED);
            if (v != s)
                AddEdgetoMST(V[v], v);         // Add edge to MST 向MST中添加边
            if (D[v] == INFINITY) return;    // Unreachable vertices 不可达节点
            for (w = G->first(v); w < G->n(); w = G->next(v, w))
                if (D[w] > G->weight(v, w)) {
                    D[w] = G->weight(v, w);       // Update distance 更新距离
                    V[w] = v;                    // Where it came from记录节点
                }
        }
}
};


#endif // GRAPH_TEST_H_INCLUDED

3.graph.h(图像的ADT文件)

// From the software distribution accompanying the textbook
// "A Practical Introduction to Data Structures and Algorithm Analysis,
// Third Edition (C++)" by Clifford A. Shaffer.
// Source code Copyright (C) 2007-2011 by Clifford A. Shaffer.

// Graph abstract class. This ADT assumes that the number
// of vertices is fixed when the graph is created.
#ifndef GRAPH
#define GRAPH

class Graph {
private:
  void operator =(const Graph&) {}     // Protect assignment
  Graph(const Graph&) {}         // Protect copy constructor

public:
  Graph() {}          // Default constructor
  virtual ~Graph() {} // Base destructor

  // Initialize a graph of n vertices
  virtual void Init(int n) =0;

  // Return: the number of vertices and edges
  virtual int n() =0;
  virtual int e() =0;

  // Return v's first neighbor
  virtual int first(int v) =0;

 // Return v's next neighbor
  virtual int next(int v, int w) =0;

  // Set the weight for an edge
  // i, j: The vertices
  // wgt: Edge weight
  virtual void setEdge(int v1, int v2, int wght) =0;

  // Delete an edge
  // i, j: The vertices
  virtual void delEdge(int v1, int v2) =0;

  // Determine if an edge is in the graph
  // i, j: The vertices
  // Return: true if edge i,j has non-zero weight
  virtual bool isEdge(int i, int j) =0;

  // Return an edge's weight
  // i, j: The vertices
  // Return: The weight of edge i,j, or zero
  virtual int weight(int v1, int v2) =0;

  // Get and Set the mark value for a vertex
  // v: The vertex
  // val: The value to set
  virtual int getMark(int v) =0;
  virtual void setMark(int v, int val) =0;

  virtual int getInDegree(int v) = 0;
  virtual int getOutDegree(int v) = 0;
};
#endif // GRAPH

4.grlist.h(若使用链表储存,需同时包含grlist.h、list.h和llist.h)

// From the software distribution accompanying the textbook
// "A Practical Introduction to Data Structures and Algorithm Analysis,
// Third Edition (C++)" by Clifford A. Shaffer.
// Source code Copyright (C) 2007-2011 by Clifford A. Shaffer.

// Include this file to access Graph representation implemented using an
// Adjacency List
# ifndef GRLIST
# define GRLIST

#include 
#include 
#include 
// Used by the mark array
#define UNVISITED 0
#define VISITED 1

#include "link.h"
#include "llist.h"

#include "graph.h"


// Edge class for Adjacency List graph representation
class Edge
{
    int vert, wt;
public:
    Edge()
    {
        vert = -1;
        wt = -1;
    }
    Edge(int v, int w)
    {
        vert = v;
        wt = w;
    }
    int vertex()
    {
        return vert;
    }
    int weight()
    {
        return wt;
    }
};



class Graphl : public Graph
{
private:
    List** vertex;        // List headers
    int numVertex, numEdge;     // Number of vertices, edges
    int *mark;                  // Pointer to mark array
public:
    Graphl(int numVert)
    {
        Init(numVert);
    }

    ~Graphl()         // Destructor
    {
        delete [] mark; // Return dynamically allocated memory
        for (int i = 0; i < numVertex; i++) delete [] vertex[i];
        delete [] vertex;
    }

    void Init(int n)
    {
        int i;
        numVertex = n;
        numEdge = 0;
        mark = new int[n];  // Initialize mark array
        for (i = 0; i < numVertex; i++) mark[i] = UNVISITED;
        // Create and initialize adjacency lists
        vertex = (List**) new List*[numVertex];
        for (i = 0; i < numVertex; i++)
            vertex[i] = new LList();
    }

    int n()
    {
        return numVertex;    // Number of vertices
    }
    int e()
    {
        return numEdge;    // Number of edges
    }

    int first(int v)   // Return first neighbor of "v"
    {
        if (vertex[v]->length() == 0)
            return numVertex;      // No neighbor
        vertex[v]->moveToStart();
        Edge it = vertex[v]->getValue();
        return it.vertex();
    }

    // Get v's next neighbor after w
    int next(int v, int w)
    {
        Edge it;
        if (isEdge(v, w))
        {
            if ((vertex[v]->currPos() + 1) < vertex[v]->length())
            {
                vertex[v]->next();
                it = vertex[v]->getValue();
                return it.vertex();
            }
        }
        return n(); // No neighbor
    }
    // Set edge (i, j) to "weight"
    void setEdge(int i, int j, int weight)
    {
//   Assert(weight>0, "May not set weight to 0");
        assert(weight > 0);
        Edge currEdge(j, weight);
        if (isEdge(i, j))   // Edge already exists in graph
        {
            vertex[i]->remove();
            vertex[i]->insert(currEdge);
        }
        else   // Keep neighbors sorted by vertex index
        {
            numEdge++;
            for (vertex[i]->moveToStart();
                    vertex[i]->currPos() < vertex[i]->length();
                    vertex[i]->next())
            {
                Edge temp = vertex[i]->getValue();
                if (temp.vertex() > j) break;
            }
            vertex[i]->insert(currEdge);
        }
    }

    void delEdge(int i, int j)    // Delete edge (i, j)
    {
        if (isEdge(i, j))
        {
            vertex[i]->remove();
            numEdge--;
        }
    }

    bool isEdge(int i, int j)   // Is (i,j) an edge?
    {
        Edge it;
        for (vertex[i]->moveToStart();
                vertex[i]->currPos() < vertex[i]->length();
                vertex[i]->next())              // Check whole list
        {
            Edge temp = vertex[i]->getValue();
            if (temp.vertex() == j) return true;
        }
        return false;
    }

    int weight(int i, int j)   // Return weight of (i, j)
    {
        Edge curr;
        if (isEdge(i, j))
        {
            curr = vertex[i]->getValue();
            return curr.weight();
        }
        else return 0;
    }

    int getMark(int v)
    {
        return mark[v];
    }
    void setMark(int v, int val)
    {
        mark[v] = val;
    }

    int getInDegree(int v)   // 求顶点v的入度
    {
        int result = 0;

        //............... 在此行以下插入补充代码
         for (int i = 0; i < numVertex; i++) {
            if (isEdge(i, v)) result++;
        }


        //............... 在此行以上插入补充代码
        return result;
    }

    int getOutDegree(int v)    // 求顶点v的出度
    {
        int result=0;
         //............... 在此行以下插入补充代码
        for (int i = 0; i < numVertex; i++) {
            if (isEdge(v, i)) result++;
        }


        //............... 在此行以上插入补充代码
        return result;
    }
};

# endif // GRLIST

5.list.h(若使用链表储存,需同时包含grlist.h、list.h和llist.h)

#ifndef LIST
#define LIST
template  class List { // List ADT
private:
  void operator =(const List&) {}      // Protect assignment   
  List(const List&) {}           // Protect copy constructor 
public:
  List() {}          // 默认构造函数 
  virtual ~List() {} // 基本的析构函数 

  // 从列表中清除内容,让它空着
  virtual void clear() = 0;

  // 在当前位置插入一个元素
  // item: 要插入的元素
  virtual void insert(const E& item) = 0;

  // 在列表的最后添加一个元素 
  // item: 要添加的元素 
  virtual void append(const E& item) = 0;

  // 删除和返回当前元素 
  // Return: 要删除的元素 
  virtual E remove() = 0;

  // 将当前位置设置为列表的开始
  virtual void moveToStart() = 0;

  // 将当前位置设置为列表的末尾 
  virtual void moveToEnd() = 0;

  // 将当前位置左移一步,如果当前位置在首位就不变 
  virtual void prev() = 0;

  // 将当前位置右移一步,如果当前位置在末尾就不变 
  virtual void next() = 0;

  // 返回列表当前元素个数 
  virtual int length() const = 0;

  // 返回当前位置 
  virtual int currPos() const = 0;

  // 设置当前位置 
  // pos: 要设置的当前位置 
  virtual void moveToPos(int pos) = 0;

  // Return: 当前位置的元素 
  virtual const E& getValue() const = 0;
};
#endif

6.llist.h(若使用链表储存,需同时包含grlist.h、list.h和llist.h)

#ifndef LIST
#define LIST
template  class List { // List ADT
private:
  void operator =(const List&) {}      // Protect assignment   
  List(const List&) {}           // Protect copy constructor 
public:
  List() {}          // 默认构造函数 
  virtual ~List() {} // 基本的析构函数 

  // 从列表中清除内容,让它空着
  virtual void clear() = 0;

  // 在当前位置插入一个元素
  // item: 要插入的元素
  virtual void insert(const E& item) = 0;

  // 在列表的最后添加一个元素 
  // item: 要添加的元素 
  virtual void append(const E& item) = 0;

  // 删除和返回当前元素 
  // Return: 要删除的元素 
  virtual E remove() = 0;

  // 将当前位置设置为列表的开始
  virtual void moveToStart() = 0;

  // 将当前位置设置为列表的末尾 
  virtual void moveToEnd() = 0;

  // 将当前位置左移一步,如果当前位置在首位就不变 
  virtual void prev() = 0;

  // 将当前位置右移一步,如果当前位置在末尾就不变 
  virtual void next() = 0;

  // 返回列表当前元素个数 
  virtual int length() const = 0;

  // 返回当前位置 
  virtual int currPos() const = 0;

  // 设置当前位置 
  // pos: 要设置的当前位置 
  virtual void moveToPos(int pos) = 0;

  // Return: 当前位置的元素 
  virtual const E& getValue() const = 0;
};
#endif

8.grmat.h(若使用数组储存,则只需包含此文件)

// From the software distribution accompanying the textbook
// "A Practical Introduction to Data Structures and Algorithm Analysis,
// Third Edition (C++)" by Clifford A. Shaffer.
// Source code Copyright (C) 2007-2011 by Clifford A. Shaffer.

// Include this file to access Graph representation implemented using an
// Adjacency Matrix.
#ifndef GRMATH
#define GRMATH

#include 
#include 
#include 
// Used by the mark array
#define UNVISITED 0
#define VISITED 1

#include "graph.h"

// Implementation for the adjacency matrix representation
class Graphm : public Graph
{
private:
    int numVertex, numEdge; // Store number of vertices, edges
    int **matrix;           // Pointer to adjacency matrix
    int *mark;              // Pointer to mark array
public:
    Graphm(int numVert)     // Constructor
    {
        Init(numVert);
    }

    ~Graphm()         // Destructor
    {
        delete [] mark; // Return dynamically allocated memory
        for (int i = 0; i < numVertex; i++)
            delete [] matrix[i];
        delete [] matrix;
    }

    void Init(int n)   // Initialize the graph
    {
        int i;
        numVertex = n;
        numEdge = 0;
        mark = new int[n];     // Initialize mark array
        for (i = 0; i < numVertex; i++)
            mark[i] = UNVISITED;
        matrix = (int**) new int*[numVertex]; // Make matrix
        for (i = 0; i < numVertex; i++)
            matrix[i] = new int[numVertex];
        for (i = 0; i < numVertex; i++) // Initialize to 0 weights
            for (int j = 0; j < numVertex; j++)
                matrix[i][j] = 0;
    }

    int n()
    {
        return numVertex;    // Number of vertices
    }
    int e()
    {
        return numEdge;    // Number of edges
    }

    // Return first neighbor of "v"
    int first(int v)
    {
        for (int i = 0; i < numVertex; i++)
            if (matrix[v][i] != 0) return i;
        return numVertex;           // Return n if none
    }

    // Return v's next neighbor after w
    int next(int v, int w)
    {
        for(int i = w + 1; i < numVertex; i++)
            if (matrix[v][i] != 0)
                return i;
        return numVertex;           // Return n if none
    }

    // Set edge (v1, v2) to "wt"
    void setEdge(int v1, int v2, int wt)
    {
//   Assert(wt>0, "Illegal weight value");
        assert(wt > 0);
        if (matrix[v1][v2] == 0) numEdge++;
        matrix[v1][v2] = wt;
    }

    void delEdge(int v1, int v2)   // Delete edge (v1, v2)
    {
        if (matrix[v1][v2] != 0) numEdge--;
        matrix[v1][v2] = 0;
    }

    bool isEdge(int i, int j) // Is (i, j) an edge?
    {
        return matrix[i][j] != 0;
    }

    int weight(int v1, int v2)
    {
        return matrix[v1][v2];
    }
    int getMark(int v)
    {
        return mark[v];
    }
    void setMark(int v, int val)
    {
        mark[v] = val;
    }

    int getInDegree(int v)   // 求顶点v的入度
    {
        int result = 0;

        //............... 在此行以下插入补充代码
        for(int i=0;i

9.link.h(链接文件)

#include  
template  class Link {
public:
  E element;      // 结点值 
  Link *next;        // 结点指针:在链表中指向下一结点
  // 构造函数
  Link(const E& elemval, Link* nextval =NULL)
    { element = elemval;  next = nextval; }
  Link(Link* nextval =NULL) { next = nextval; }
};

你可能感兴趣的:(#,算法,c++,开发语言)