【PAT顶级】1001 Battle Over Cities - Hard Version

刷题笔记

1. 万能头文件:

#include <bits/stdc++.h>

2. 初始化生成连续整数:0、1、2、...、N

iota(fa, fa + n + 1, 0); // 容器的起始迭代器、容器的结束迭代器以及要填充的初始值。

3. 小于号重载:按cost从大到小排序

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

4. 匿名函数

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

5. 并查集模板

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<bits/stdc++.h>
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<int> max_city; // 最终结果有哪些城市需要保护
int costs[505];
vector<Edge> 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<bits/stdc++.h>
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<int> max_city; // 最终结果有哪些城市需要保护
int costs[505], fa[505]; // 记录每个城市对应的代价
vector<Edge> 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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值