1001 Battle Over Cities - Hard Version

一、前言

刚接触PAT顶级的题目,感觉困难的点在于,对题目能够有一个准确的把握没那么容易。到底用哪种算法思想也需要花很多时间甄别。

再者就是锻炼编码能力,后期的优化真的很重要,在比较不同的实现方式的优劣的过程中,精简我们的代码。

二、刷题笔记

1. #include

关于引入这个头文件,有时候会报错是以下原因:

这是因为你使用了`#include `这个头文件,这个头文件在一些编译器中是不存在的。这个头文件是一种非标准的头文件,它包含了所有标准库的头文件,但它并不是标准C++的一部分。

如果你想使用标准库的话,你可以直接包含相应的头文件,比如`#include `、`#include `等等,根据你的需要包含相应的头文件。

另外,使用`#include `这个头文件也是不推荐的,因为它会导致编译时间变长。推荐的做法是只包含你需要的头文件,这样可以提高编译速度。

2. iota(fa, fa + n + 1, 0); // 初始化并查集

这段代码使用了C++标准库中的`iota`函数,它的作用是将一个指定范围内的值依次填充到一个容器中。

具体来说,`iota`函数接受三个参数:容器的起始迭代器、容器的结束迭代器以及要填充的初始值。

在这段代码中,`iota`函数被用于填充一个名为`father`的容器,容器的起始迭代器为`father`,结束迭代器为`father + N + 1`,初始值为0。

换句话说,这段代码将从`father`开始的`N+1`个元素依次填充为0、1、2、...、N。

3. 结构体排序的两种实现方式

3.1 小于号重载

struct Edge {
    int a, b, cost;
    bool operator< (const Edge &b) {
        return this->cost < b.cost;
    }
};
vector repair;
sort(repair.begin(), repair.end());

3.2 匿名函数

struct Edge {
    int a, b, cost;
};
vector repair;
sort(repair.begin(), repair.end(), [](const Edge &a, const Edge &b) {
    return a.cost < b.cost;
});

4. 并查集模板

struct UnionSet {
    int *fa, n; 
    UnionSet(int n) : n(n) {
        fa = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
        }
    }
    int find(int x) { 
        if (fa[x] == x) return x;
        return fa[x] = find(fa[x]);  // 路径压缩
    }
    void merge(int a, int b) {
        int ra = find(a), rb = find(b);
        if (ra == rb) return ;
        fa[ra] = rb;
        return ;
    }
    int count() { // 统计类的个数
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            if (fa[i] == i) cnt++;
        }
        return cnt;
    }
};

三、题解代码

/*************************************************************************
        > File Name: 1001.cpp
        > Author: jby
        > Mail: 
        > Created Time: Tue 22 Aug 2023 10:00:25 PM CST
 ************************************************************************/

// 这是一个图问题、连通问题。
// 用到了最小生成树的思想。
#include
using namespace std;

struct UnionSet {
    int *fa, n; 
    UnionSet(int n) : n(n) {
        fa = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
        }
    }
    int find(int x) {
        if (fa[x] == x) return x;
        return fa[x] = find(fa[x]); 
    }
    void merge(int a, int b) {
        int ra = find(a), rb = find(b);
        if (ra == rb) return ;
        fa[ra] = rb;
        return ;
    }
    int count() {
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            if (fa[i] == i) cnt++;
        }
        return cnt;
    }
};
struct Edge {
    int a, b, cost;
    bool operator< (const Edge &b) {
        return this->cost < b.cost;
    }
};
int n, m, max_cost;   // n 城市数量, m 铁路数量, max_cost最大代价
vector max_city; // 最终结果有哪些城市需要保护
int costs[505];
vector repair, use;

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++) {
        int a, b, c, d;
        scanf("%d%d%d%d", &a, &b, &c, &d);
        if (d == 0) repair.push_back(Edge{a, b, c});
        else use.push_back(Edge{a, b, c});
    }
    sort(repair.begin(), repair.end());
    for (int i = 1; i <= n; i++) {
        UnionSet u(n);
        for (auto x : use) {
            int fa = u.find(x.a), fb = u.find(x.b);
            if (x.a != i && x.b != i && fa != fb) {
                u.merge(x.a, x.b);
            }
        }
        for (auto x : repair) {
            int fa = u.find(x.a), fb = u.find(x.b);
            if (x.a != i && x.b != i && fa != fb) {
                u.merge(x.a, x.b);
                costs[i] += x.cost;
            }
        }
        if (u.count() > 2) costs[i] = INT_MAX; // 说明当前城市被攻占了,其余城市不可能连通,最重要。
    }
    for (int i = 1; i <= n; i++) {
        if (costs[i] > max_cost) {
            max_city.clear();
            max_cost = costs[i];
            max_city.push_back(i);
        } else if (costs[i] > 0 && costs[i] == max_cost) {
            max_city.push_back(i);
        }
    }
    if (max_city.size() == 0) {
        printf("%d\n", 0);
    } else {
        sort(max_city.begin(), max_city.end());
        for (int i = 0; i < max_city.size(); i++) {
            i && printf(" ");
            printf("%d", max_city[i]);
        }
        printf("\n");
    }
    return 0;
}

下面这版代码,我把并查集拆掉了,并取消了重载小于号,转而用上了匿名函数。

// 这是一个图问题、连通问题。
// 用到了最小生成树的思想。
#include
using namespace std;
struct Edge {
    int a, b, cost;
    // 可以用重载小于号,也可以在sort方法中用匿名函数,本质都是一样的。
    // bool operator< (const Edge &b) {
    //     return this->cost < b.cost;
    // }
};
int n, m, max_cost;   // n 城市数量, m 铁路数量, max_cost最大代价
vector max_city; // 最终结果有哪些城市需要保护
int costs[505], fa[505]; // 记录每个城市对应的代价
vector repair, use; 
int count() {
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (fa[i] == i) cnt++;
    }
    return cnt;
}
int find(int x) {
    if (fa[x] == x) return x;
    return fa[x] = find(fa[x]); // 没必要为了统计类个数而牺牲路径优化。
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++) {
        int a, b, c, d;
        scanf("%d%d%d%d", &a, &b, &c, &d);
        if (d == 0) {
            repair.push_back(Edge{a, b, c});
        } else {
            use.push_back(Edge{a, b, c});
        }
    }
    sort(repair.begin(), repair.end(), [](const Edge &a, const Edge &b) {
        return a.cost < b.cost;
    });
    for (int i = 1; i <= n; i++) {
        iota(fa, fa + n + 1, 0); // 初始化并查集
        for (auto x : use) {
            int ra = find(x.a), rb = find(x.b);
            if (x.a != i && x.b != i && ra != rb) {
                fa[ra] = rb;
            }
        }
        for (auto x : repair) {
            int ra = find(x.a), rb = find(x.b);
            if (x.a != i && x.b != i && ra != rb) {
                fa[ra] = rb;
                costs[i] += x.cost;
            }
        }
        if (count() > 2) {
            costs[i] = INT_MAX;
        }
    }
    for (int i = 1; i <= n; i++) {
        if (costs[i] > max_cost) {
            max_cost = costs[i];
            max_city.clear();
            max_city.push_back(i);
        } else if (costs[i] > 0 && costs[i] == max_cost) {
            max_city.push_back(i);
        }
    }
    if (max_city.empty()) {
        printf("0\n");
    } else {
        for (int i = 0; i < max_city.size(); i++) {
            i && printf(" ");
            printf("%d", max_city[i]);
        }
        printf("\n");
    }
    return 0;
}

你可能感兴趣的:(PAT顶级,算法)