实验09 图连通与最小生成树

文章目录

  • A. 图的应用之——图的连通
    • 题目描述
    • 输入
    • 输出
    • 输入样例1
    • 输出
    • 代码
  • B. DS图—最小生成树
    • 题目描述
    • 输入
    • 输出
    • 输入样例1
    • 输出
    • 代码
  • C. 图综合练习--拓扑排序
    • 题目描述
    • 输入
    • 输出
    • 输入样例1
    • 输出1
    • 代码
  • D. DS图—图的连通分量
    • 题目描述
    • 输入
    • 输出
    • 输入样例1
    • 输出
    • 代码
  • E. 图的顶点可达闭包
    • 题目描述
    • 输入
    • 输出
    • 输入样例1
    • 输出
    • 代码


A. 图的应用之——图的连通

题目描述

给定一个图的邻接矩阵,请判断该图是否是连通图。连通图:任意两个顶点之间都有路径。

–程序要求–
若使用C++只能include一个头文件iostream;若使用C语言只能include一个头文件stdio
程序中若include多过一个头文件,不看代码,作0分处理
不允许使用第三方对象或函数实现本题的要求

输入

第1行输入一个整数k,表示有k个测试数据

第2行输入一个整数n,表示有n个结点

从第3行起到第n+2行输入一个邻接矩阵,其中Matrix[i,j]=1表示第i,j个结点之间有边,否则不存在边。

接下来是第2到第k个测试数据的结点数和邻接矩阵

输出

输出Yes or No表示图是否是强连通图

输入样例1

2
4
0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0
7
0 1 0 0 0 0 0
0 0 1 1 0 0 0
1 0 0 0 0 0 0
1 0 1 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 0 0 1 0 1 0

输出

Yes
No

代码

#include

using namespace std;
const int MaxLen = 20;
int n;
int da[20][20] = {0};
int da2[20][20] = {0};

void change() {
    int i, j;
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            da2[i][j] = da[j][i];
}

class Map {
private:
    bool Visit[MaxLen];
    int Matrix[MaxLen][MaxLen];
    int Vexnum;

    void DFS(int v);
    void DFS2(int v);
    int count = 0;
    int c2=0;
public:
    void SetMatrix(int vnum, int mx[MaxLen][MaxLen]);

    void DFSTraverse();
};

void Map::SetMatrix(int vnum, int (*mx)[20]) {
    int i, j;
    Vexnum = vnum;
    for (i = 0; i < MaxLen; i++)
        for (j = 0; j < MaxLen; j++)
            Matrix[i][j] = 0;

    for (i = 0; i < Vexnum; i++)
        for (j = 0; j < Vexnum; j++)
            Matrix[i][j] = mx[i][j];
}

void Map::DFSTraverse() {
    int i;
    for (i = 0; i < Vexnum; i++)
        Visit[i] = false;
    DFS(0);
    if (count == Vexnum) {
        change();
        for (i = 0; i < Vexnum; i++)
            Visit[i] = false;
        DFS2(0);
        if(c2==Vexnum) cout << "Yes\n";
        else cout << "No\n";
    } else cout << "No\n";
}
void Map::DFS2(int v) {
    int w, i, k;
    Visit[v] = true;
    c2++;
    int *AdjVex = new int[Vexnum];
    for (i = 0; i < Vexnum; i++)
        AdjVex[i] = -1;
    k = 0;
    for (i = 0; i < Vexnum; i++) {
        if (da2[v][i] == 1) AdjVex[k++] = i;
    }
    i = 0;
    for (w = AdjVex[i]; w != -1; w = AdjVex[++i]) {
        if (!Visit[w]) DFS2(w);
    }
    delete[]AdjVex;
}
void Map::DFS(int v) {
    int w, i, k;
    Visit[v] = true;
    count++;
    int *AdjVex = new int[Vexnum];
    for (i = 0; i < Vexnum; i++)
        AdjVex[i] = -1;
    k = 0;
    for (i = 0; i < Vexnum; i++) {
        if (Matrix[v][i] == 1) AdjVex[k++] = i;
    }
    i = 0;
    for (w = AdjVex[i]; w != -1; w = AdjVex[++i]) {
        if (!Visit[w]) DFS(w);
    }
    delete[]AdjVex;
}

int main() {
    int t, i, j;
    cin >> t;
    while (t--) {
        cin >> n;
        for (i = 0; i < n; i++)
            for (j = 0; j < n; j++)
                cin >> da[i][j];
        Map p;
        p.SetMatrix(n, da);
        p.DFSTraverse();
    }
}

B. DS图—最小生成树

题目描述

根据输入创建无向网。分别用Prim算法和Kruskal算法构建最小生成树。(假设:输入数据的最小生成树唯一。)

输入

顶点数n

n个顶点

边数m

m条边信息,格式为:顶点1顶点2权值

Prim算法的起点v

输出

输出最小生成树的权值之和

对两种算法,按树的生长顺序,输出边信息(Kruskal中边顶点按数组序号升序输出)

输入样例1

6
v1 v2 v3 v4 v5 v6
10
v1 v2 6
v1 v3 1
v1 v4 5
v2 v3 5
v2 v5 3
v3 v4 5
v3 v5 6
v3 v6 4
v4 v6 2
v5 v6 6
v1

输出

15
prim:
v1 v3 1
v3 v6 4
v6 v4 2
v3 v2 5
v2 v5 3
kruskal:
v1 v3 1
v4 v6 2
v2 v5 3
v3 v6 4
v2 v3 5

代码

#include
using namespace std;
string Name[1000];
struct Edge {
    int u, v, dis;
    bool operator <(const Edge& e2) const {
        Edge e1 = *this;
        if (e1.dis == e2.dis) {
            if (e1.u == e2.u)
                return e1.v > e2.v;
            return e1.u > e2.u;
        }
        return e1.dis > e2.dis;
    }
}edge[1000];
bool cmp(Edge e1, Edge e2) {
    if (e1.dis == e2.dis) {
        if (e1.u == e2.u)
            return e1.v < e2.v;
        return e1.u < e2.u;
    }
    return e1.dis < e2.dis;
}
int fa[1000];
int find(int x) {
    if (fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}
void merge(int a, int b) {
    fa[find(a)] = find(b);
    return;
}
int visit[1000];
int main()
{
    int n, m;
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> Name[i];
    cin >> m;
    string s1, s2;
    int dis;
    for (int i = 1; i <= m; i++) {
        cin >> s1 >> s2 >> dis;
        int u = 0, v = 0;
        for (int j = 1; j <= n; j++) {
            if (s1 == Name[j]) u = j;
            if (s2 == Name[j]) v = j;
        }
        edge[i].u = u;
        edge[i].v = v;
        edge[i].dis = dis;
        edge[i + m].u = v;
        edge[i + m].v = u;
        edge[i + m].dis = dis;
    }
    sort(edge + 1, edge + 1 + 2 * m, cmp);
    int tot = 0;
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
    }
    for (int i = 1; i <= 2 * m; i++) {
        int u = edge[i].u, v = edge[i].v;
        if (find(u) != find(v)) {
            merge(u, v);
            tot += edge[i].dis;
            //cout << Name[u] << ' ' << Name[v] << ' '<
        }
    }
    cout << tot << endl;

    cout << "prim:" << endl;
    cin >> s1;
    int start;
    for (int i = 1; i <= n; i++) {
        if (s1 == Name[i]) {
            start = i;
            break;
        }
    }
    priority_queue<Edge> q;
    for (int i = 1; i <= n; i++)
        visit[i] = 0;
    visit[start] = 1;
    for (int i = 1; i <= 2 * m; i++) {
        if (edge[i].u == start) q.push(edge[i]);
    }
    while (!q.empty()) {
        Edge t = q.top();
        q.pop();
        if (visit[t.v]) continue;
        cout << Name[t.u] << ' ' << Name[t.v] << ' ' << t.dis << endl;
        visit[t.v] = 1;
        for (int i = 1; i <= 2 * m; i++) {
            if (edge[i].u == t.v && !visit[edge[i].v])
                q.push(edge[i]);
        }
    }
    cout << "kruskal:" << endl;
    for (int i = 1; i <= n; i++) {
        fa[i] = i;
    }
    for (int i = 1; i <= 2 * m; i++) {
        int u = edge[i].u, v = edge[i].v;
        if (find(u) != find(v)) {
            merge(u, v);
            //tot += edge[i].dis;
            cout << Name[u] << ' ' << Name[v] << ' ' << edge[i].dis << endl;
        }
    }

}

C. 图综合练习–拓扑排序

题目描述

已知有向图,顶点从0开始编号,求它的求拓扑有序序列。

拓扑排序算法:给出有向图邻接矩阵
1.逐列扫描矩阵,找出入度为0且编号最小的顶点v

2.输出v,并标识v已访问

3.把矩阵第v行全清0

重复上述步骤,直到所有顶点输出为止

–程序要求–
若使用C++只能include一个头文件iostream;若使用C语言只能include一个头文件stdio
程序中若include多过一个头文件,不看代码,作0分处理
不允许使用第三方对象或函数实现本题的要求

输入

第一行输入一个整数t,表示有t个有向图

第二行输入n,表示图有n个顶点

第三行起,输入n行整数,表示图对应的邻接矩阵

以此类推输入下一个图的顶点数和邻接矩阵

输出

每行输出一个图的拓扑有序序列

输入样例1

2
5
0 1 0 1 1
0 0 1 0 0
0 0 0 0 1
0 0 1 0 0
0 0 0 0 0
7
0 0 0 0 0 0 0
1 0 1 1 0 0 0
1 0 0 0 0 0 0
1 0 1 0 0 0 0
0 0 0 0 0 1 1
0 1 0 0 0 0 0
0 0 0 1 0 1 0

输出1

0 1 3 2 4
4 6 5 1 3 2 0

代码

#include

using namespace std;
int Visited[100];
int a[100][100], n, k;
int JiLu[100] = {0};

void clea(int aa) {
    int i;
    for (i = 0; i < n; i++)
        a[aa][i] = 0;
}

int findzero() {
    int i, j, tag;
    for (i = 0; i < n; i++) { //列数
        if (!Visited[i]) {
            for (j = 0, tag = 1; j < n; j++) {//行数
                if (a[j][i] == 1) {
                    tag = 0;
                    break;
                }
            }
            if (tag) {
                JiLu[k++] = i;
                return 1;
            } else continue;
        }
    }
    return 0;
}

int main() {
    int t;
    cin >> t;
    while (t--) {
        int i, j;
        k = 0;
        for(i=0;i<n;i++)
            Visited[i]=0;
        cin >> n;
        for (i = 0; i < n; i++)
            for (j = 0; j < n; j++)
                cin >> a[i][j];
        while (findzero()) {
            for (i = 0; i < k; i++, k--) {
                cout << JiLu[i] << ' ';
                Visited[JiLu[i]] = 1;
                clea(JiLu[i]);
            }
        }
        cout << endl;
    }

}

D. DS图—图的连通分量

题目描述

输入无向图顶点信息和边信息,创建图的邻接矩阵存储结构,计算图的连通分量个数。

输入

测试次数t

每组测试数据格式如下:

第一行:顶点数 顶点信息

第二行:边数

第三行开始,每行一条边信息

输出

每组测试数据输出,顶点信息和邻接矩阵信息

输出图的连通分量个数,具体输出格式见样例。

每组输出直接用空行分隔。

输入样例1

3
4 A B C D
2
A B
A C
6 V1 V2 V3 V4 V5 V6
5
V1 V2
V1 V3
V2 V4
V5 V6
V3 V5
8 1 2 3 4 5 6 7 8
5
1 2
1 3
5 6
5 7
4 8

输出

A B C D
0 1 1 0
1 0 0 0
1 0 0 0
0 0 0 0
2

V1 V2 V3 V4 V5 V6
0 1 1 0 0 0
1 0 0 1 0 0
1 0 0 0 1 0
0 1 0 0 0 0
0 0 1 0 0 1
0 0 0 0 1 0
1

1 2 3 4 5 6 7 8
0 1 1 0 0 0 0 0
1 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1
0 0 0 0 0 1 1 0
0 0 0 0 1 0 0 0
0 0 0 0 1 0 0 0
0 0 0 1 0 0 0 0
3

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int da[100][100];
int n;
int Visited[100];

int found(string a, string* s) {
    int i;
    for (i = 0; i < n; i++) {
        if (a == s[i]) return i;
    }
}

void DFS(int v) {
    int w, i, k;
    Visited[v] = 1;

    int* AdjVex = new int[n];
    for (i = 0; i < n; i++)
        AdjVex[i] = -1;
    k = 0;
    for (i = 0; i < n; i++) {
        if (da[v][i] == 1) AdjVex[k++] = i;
    }
    i = 0;
    for (w = AdjVex[i]; w != -1; w = AdjVex[++i]) {
        if (!Visited[w]) DFS(w);
    }
    delete[]AdjVex;
}
void DFSTraverse() {
    int i, cnt = 0;
    for (i = 0; i < n; i++)
        if (!Visited[i]) {
            DFS(i);
            cnt++;
        }
    cout << cnt << endl;
}
int main() {
    int t;
    cin >> t;
    while (t--) {
        int i, m, k1, k2, j;
        cin >> n;
        for (i = 0; i < n; i++)
            Visited[i] = 0;
        string* str = new string[n];
        for (i = 0; i < n; i++)
            cin >> str[i];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                da[i][j] = 0;
            }
        }
        cin >> m;
        for (j = 0; j < m; j++) {
            string s1, s2;
            cin >> s1 >> s2;
            k1 = found(s1, str);
            k2 = found(s2, str);
            da[k1][k2] = 1;
            da[k2][k1] = 1;
        }
        for (i = 0; i < n; i++) {
            if (i < n - 1)
                cout << str[i] << ' ';
            else cout << str[i] << endl;
        }
        for (i = 0; i < n; i++)
            for (j = 0; j < n; j++)
                if (j < n - 1) cout << da[i][j] << ' ';
                else cout << da[i][j] << endl;
        DFSTraverse();
        cout << endl;
    }
}

E. 图的顶点可达闭包

题目描述

给定有向图的邻接矩阵A,其元素定义为:若存在顶点i到顶点j的有向边则A[i,j]=1,若没有有向边则A[i,j]=0。试求A的可达闭包矩阵A*,其元素定义为:若存在顶点i到顶点j的有向路径则A*[i,j]=1,若没有有向路径则A*[i,j]=0。

输入

第1行顶点个数n

第2行开始的n行有向图的邻接矩阵,元素之间由空格分开

输出

有向图的可达闭包矩阵A*,元素之间由空格分开

输入样例1

4
0 1 0 1
0 0 1 0
0 0 0 0
0 0 0 0

输出

0 1 1 1
0 0 1 0
0 0 0 0
0 0 0 0

代码

#include
#include 

using namespace std;
int n, mm = 0;
int data2[100][100];
int data[100][100];
int Visited[100];

void BFS(int a, int v) {
    int w, i, k, u;
//    data2[a][v] = 1;
    int *AdjVex = new int[n];
    for (i = 0; i < n; i++)
        AdjVex[i] = -1;
    queue<int> Q;
    if (!Visited[v]) {
//        Visited[v] = 1;
        Q.push(v);
        while (!Q.empty()) {
            u = Q.front();
            Q.pop();
            k = 0;
            for (i = 0; i < n; i++)
                if (data[u][i] == 1) AdjVex[k++] = i;
            i = 0;
            for (w = AdjVex[i]; w >= 0; w = AdjVex[++i]) {
                if (!Visited[w]) {
                    Visited[w] = 1;
                    data2[a][w] = 1;
                    Q.push(w);
                }
            }
        }
    }
    delete[]AdjVex;
}

void BFSTraverse(int a) {
    if (!Visited[a])
        BFS(a, a);
}

void clea() {
    int i;
    for (i = 0; i < n; i++)
        Visited[i] = 0;
}

int main() {
    cin >> n;
    int i, j;
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            cin >> data[i][j];

    for (i = 0; i < n; i++) {
        clea();
        BFSTraverse(i);
    }

    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            if (j < n - 1) cout << data2[i][j] << ' ';
            else cout << data2[i][j] << endl;
}

你可能感兴趣的:(数据结构,算法,图论,c++,数据结构)