📜  门| GATE CS 2021 |套装2 |第62章(1)

📅  最后修改于: 2023-12-03 14:58:21.380000             🧑  作者: Mango

门| GATE CS 2021 |套装2 |第62章

本套装是针对2021年计算机科学门考试的准备套装之一,其中包括了第62章的考点内容。

考点内容

第62章主要包括以下几个方面:

  • 优先队列的基本操作
  • 哈夫曼编码
  • 最小生成树算法:Prim和Kruskal算法
  • 单源最短路径算法:Dijkstra和Bellman-Ford算法
  • 全源最短路径算法:Floyd-Warshall算法
优先队列的基本操作

优先队列可以看做一个普通队列的升级版,它保证了队列中的元素按照一定规则(如大小)有序排列。其中常见的操作包括插入元素、删除最大(最小)元素、获取最大(最小)元素等。

在C++ STL中,STL提供了priority_queue模板类,实现了优先队列。其中可以通过指定比较函数,实现对队列中元素的大小比较规则。

代码片段:

#include <queue>
#include <iostream>

using namespace std;

int main() {
    priority_queue<int, vector<int>, greater<int>> pq; // 小根堆
    pq.push(5);
    pq.push(3);
    pq.push(7);
    while (!pq.empty()) {
        cout << pq.top() << " ";
        pq.pop();
    }
    // 输出结果为:3 5 7
    return 0;
}
哈夫曼编码

哈夫曼编码是一种用于压缩数据的编码方式,它通过统计字符出现的频率,构建出一棵树,从而实现对数据的压缩。其中,出现频率较高的字符被编码成长度较短的编码,出现频率较低的字符则编码成长度较长的编码。

哈夫曼编码的构建过程可以通过贪心算法实现,具体过程可以参考以下示例代码:

#include <iostream>
#include <string>
#include <queue>

using namespace std;

struct node {
    int freq;
    char ch;
    node *left, *right;
    node(int freq, char ch) : freq(freq), ch(ch), left(nullptr), right(nullptr) {}
};

struct cmp {
    bool operator()(node *a, node *b) {
        return a->freq > b->freq;
    }
};

void encode(node *root, string str, unordered_map<char, string> &huffmanCode) {
    if (root == nullptr) {
        return;
    }
    if (!root->left && !root->right) {
        huffmanCode[root->ch] = str;
    }
    encode(root->left, str + "0", huffmanCode);
    encode(root->right, str + "1", huffmanCode);
}

unordered_map<char, string> buildHuffmanTree(string text) {
    unordered_map<char, int> freq;
    for (char c : text) {
        ++freq[c];
    }
    priority_queue<node*, vector<node*>, cmp> pq;
    for (auto &f : freq) {
        pq.push(new node(f.second, f.first));
    }
    while (pq.size() > 1) {
        node *left = pq.top(); pq.pop();
        node *right = pq.top(); pq.pop();
        node *parent = new node(left->freq + right->freq, '\0');
        parent->left = left;
        parent->right = right;
        pq.push(parent);
    }
    node *root = pq.top();
    unordered_map<char, string> huffmanCode;
    encode(root, "", huffmanCode);
    return huffmanCode;
}

int main() {
    string text = "huffman coding is a better compression technique";
    unordered_map<char, string> huffmanCode = buildHuffmanTree(text);
    cout << "Huffman Codes are:\n";
    for (auto &&[ch, code] : huffmanCode) {
        cout << ch << " : " << code << '\n';
    }
    return 0;
}
最小生成树算法:Prim和Kruskal算法

最小生成树是一种用于求解图中连接所有节点的最小代价树的算法,常见的实现方式有Prim算法和Kruskal算法。其中,Prim算法通过从已选的节点中选择距离最小的节点加入,构建生成树;而Kruskal算法通过排序边,从小到大加入边直到所有节点被连接。

Prim算法的实现如下:

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

const int INF = 1e9;
const int N = 1e5 + 5;

int n, m;
int head[N], ver[N << 1], edge[N << 1], ne[N << 1], idx;
bool vis[N];
int dist[N], pre[N];

struct node {
    int v, dist;
    bool operator<(const node &rhs) const {
        return dist > rhs.dist;
    }
};

priority_queue<node> pq;

void add(int u, int v, int w) {
    ver[++idx] = v, edge[idx] = w, ne[idx] = head[u], head[u] = idx;
}

void prim() {
    pq.push({1, 0});

    for (int i = 1; i <= n; ++i) {
        dist[i] = INF;
    }

    dist[1] = 0;

    while (!pq.empty()) {
        int u = pq.top().v; pq.pop();

        if (vis[u]) {
            continue;
        }

        vis[u] = true;

        for (int i = head[u]; i != -1; i = ne[i]) {
            int v = ver[i], w = edge[i];
            if (!vis[v] && dist[v] > w) {
                dist[v] = w;
                pre[v] = u;
                pq.push({v, dist[v]});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    memset(head, -1, sizeof(head));

    cin >> n >> m;

    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w), add(v, u, w);
    }

    prim();

    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        if (dist[i] == INF) {
            cout << "impossible\n";
            return 0;
        }
        ans += dist[i];
    }

    cout << ans << '\n';

    return 0;
}

Kruskal算法的实现如下:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int N = 1e5 + 5;
const int M = 2e5 + 5;

struct node {
    int u, v, w;
    bool operator<(const node &rhs) const {
        return w < rhs.w;
    }
};

int n, m;
int p[N], cnt;
node edge[M];

int find(int x) {
    if (x != p[x]) {
        p[x] = find(p[x]);
    }
    return p[x];
}

void kruskal() {
    sort(edge + 1, edge + m + 1);

    for (int i = 1; i <= n; ++i) {
        p[i] = i;
    }

    for (int i = 1; i <= m; ++i) {
        int u = edge[i].u, v = edge[i].v, w = edge[i].w;
        int pu = find(u), pv = find(v);
        if (pu != pv) {
            p[pu] = pv;
            ++cnt;
            if (cnt == n - 1) break;
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;

    for (int i = 1; i <= m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        edge[i] = {u, v, w};
    }

    kruskal();

    int ans = 0;
    if (cnt != n - 1) {
        cout << "impossible\n";
        return 0;
    }
    for (int i = 1; i <= n; ++i) {
        if (p[i] == i) {
            ++ans;
        }
    }

    cout << ans - 1 << '\n';

    return 0;
}
单源最短路径算法:Dijkstra和Bellman-Ford算法

单源最短路径算法用于求解图中从一个节点出发到其他节点的最短路径。其中常见的实现有Dijstra算法和Bellman-Ford算法。Dijkstra算法通过贪心地选择距离目标节点距离最短的未访问节点,更新到目标节点的距离值;Bellman-Ford算法则通过遍历所有边,更新每个节点到目标节点的距离值,重复n次即可得到最短路径。

Dijkstra算法的实现如下:

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

const int N = 1e6 + 5;
const int INF = 1e9;

int n, m, s;
int head[N], ver[N], edge[N], ne[N], idx;
int dist[N];
bool vis[N];

struct node {
    int v, dist;
    bool operator<(const node &rhs) const {
        return dist > rhs.dist;
    }
};

void add(int u, int v, int w) {
    ver[++idx] = v, edge[idx] = w, ne[idx] = head[u], head[u] = idx;
}

void dijkstra() {
    priority_queue<node> pq;

    for (int i = 1; i <= n; ++i) {
        dist[i] = INF;
    }

    dist[s] = 0;
    pq.push({s, 0});

    while (!pq.empty()) {
        int u = pq.top().v; pq.pop();
        if (vis[u]) {
            continue;
        }
        vis[u] = true;
        for (int i = head[u]; i != -1; i = ne[i]) {
            int v = ver[i], w = edge[i];
            if (dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                pq.push({v, dist[v]});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m >> s;
    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w);
    }

    dijkstra();

    for (int i = 1; i <= n; ++i) {
        cout << dist[i] << " ";
    }
    cout << '\n';

    return 0;
}

Bellman-Ford算法的实现如下:

#include <iostream>
#include <vector>

using namespace std;

const int N = 1e6 + 5;
const int INF = 1e9;

struct node {
    int u, v, w;
};

int n, m, s;
int dist[N];
vector<node> edges;

void bellman_ford() {
    for (int i = 1; i <= n; ++i) {
        dist[i] = INF;
    }

    dist[s] = 0;

    for (int i = 1; i < n; ++i) {
        for (auto &edge : edges) {
            int u = edge.u, v = edge.v, w = edge.w;
            if (dist[u] != INF && dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
            }
        }
    }

    for (int i = 1; i <= n; ++i) {
        cout << dist[i] << " ";
    }
    cout << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m >> s;

    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        edges.push_back({u, v, w});
    }

    bellman_ford();

    return 0;
}
全源最短路径算法:Floyd-Warshall算法

全源最短路径算法用于求解图中任意两个节点之间的最短路径。其中常见的实现有Floyd-Warshall算法。Floyd-Warshall算法通过动态规划,遍历所有三元组(u, v, k),其中k表示中转节点,更新所有的最短路径。

Floyd-Warshall算法的实现如下:

#include <iostream>

using namespace std;

const int N = 1e6 + 5;
const int INF = 1e9;

int n, m;
int dist[N][N];

void floyd_warshall() {
    for (int k = 1; k <= n; ++k) {
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= n; ++j) {
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
            }
        }
    }

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (i == j) {
                cout << "0 ";
            } else if (dist[i][j] == INF) {
                cout << "INF ";
            } else {
                cout << dist[i][j] << " ";
            }
        }
        cout << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            dist[i][j] = INF;
        }
    }
    for (int i = 1; i <= m; ++i) {
        int u, v, w;
        cin >> u >> v >> w;
        dist[u][v] = w;
    }

    floyd_warshall();

    return 0;
}

以上是本次考试的内容,希望大家能够在考试中取得好成绩!