zoukankan      html  css  js  c++  java
  • 2021NUAA暑假集训 Day4 部分题解

    比赛链接:21.7.15-NUAA暑期集训
    比赛码:NUAAACM20210715



    A - 热浪

    单源最短路模板。

    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    
    #define INF 0x3f3f3f3f
    #define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
    
    int cnt, head[2510], dis[2510], n, m, s, t;
    bool vis[2510];
    struct Edge {
        int v, w, next;
    } edge[12500];
    
    void addEdge(int u, int v, int w) {
        edge[++cnt].v = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    void spfa(int s) {
        queue<int> q;
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= n; ++i) {
            dis[i] = INF;
        }
        dis[s] = 0;
        vis[s] = true;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            vis[u] = false;
            q.pop();
            for (int i = head[u]; i; i = edge[i].next) {
                int v = edge[i].v;
                if (dis[v] > dis[u] + edge[i].w) {
                    dis[v] = dis[u] + edge[i].w;
                    if (!vis[v]) {
                        q.push(v);
                        vis[v] = true;
                    }
                }
            }
        }
    }
    
    int main() {
        io_speed_up;
        cin >> n >> m >> s >> t;
        for (int i = 1, u, v, w; i <= m; ++i) {
            cin >> u >> v >> w;
            addEdge(u, v, w);
            addEdge(v, u, w);
        }
        spfa(s);
        cout << dis[t];
        return 0;
    }
    

    B - Silver Cow Party

    存两张图:
    第一张图正常存边,用于求从(x)返回其他点时的最短路。
    第二张图存第一张图的反向边,由于从其他点到(x)是多源单终点,存反向边则可以看作是从(x)到其他点,再求一遍最短路。
    将各点在两次最短路里的(dis)值相加,更新最大值。

    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    
    #define INF 0x3f3f3f3f
    #define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
    
    int cnt[2], head[2][1010], dis[2][1010], n, m, x;
    bool vis[1010];
    struct Edge {
        int v, w, next;
    } edge[2][100010];
    
    void addEdge(int k, int u, int v, int w) {
        edge[k][++cnt[k]].v = v;
        edge[k][cnt[k]].w = w;
        edge[k][cnt[k]].next = head[k][u];
        head[k][u] = cnt[k];
    }
    
    void spfa(int s, int k) {
        queue<int> q;
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= n; ++i) {
            dis[k][i] = INF;
        }
        dis[k][s] = 0;
        vis[s] = true;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            vis[u] = false;
            q.pop();
            for (int i = head[k][u]; i; i = edge[k][i].next) {
                int v = edge[k][i].v;
                if (dis[k][v] > dis[k][u] + edge[k][i].w) {
                    dis[k][v] = dis[k][u] + edge[k][i].w;
                    if (!vis[v]) {
                        q.push(v);
                        vis[v] = true;
                    }
                }
            }
        }
    }
    
    int main() {
        io_speed_up;
        cin >> n >> m >> x;
        for (int i = 1, u, v, w; i <= m; ++i) {
            cin >> u >> v >> w;
            addEdge(0, u, v, w);
            addEdge(1, v, u, w);
        }
        spfa(x, 0);
        spfa(x, 1);
        int ans = 0;
        for (int i = 1; i <= n; ++i) {
            ans = max(ans, dis[0][i] + dis[1][i]);
        }
        cout << ans << endl;
        return 0;
    }
    

    C - Intervals

    该题是一个差分约束系统。
    (f[i])表示在([0,i])区间内取了多少个数。
    由输入可以得到多个不等式(f[b] - f[a - 1] geq c),则在建图过程中点(a-1)有一条权值为(c)的有向边到点(b)
    同时题目本身存在隐藏限制条件(1geq f[i] - f[i-1] geq 0(0 leq i leq maxn)),即(left{egin{matrix}f[i] - f[i-1] geq 0 \f[i-1] - f[i] geq -1end{matrix} ight.),则在建图过程中每个点(i-1)有一条权值为(0)的有向边到点(i),点(i)有一条权值为(-1)的有向边到点(i-1)
    由于区间左端最小可以为(0),所以需要将数组下标加(1),防止出现下标为(-1)的情况。
    建图完成之后跑最长路即可。

    #include <iostream>
    #include <queue>
    using namespace std;
    #define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
    
    struct Edge {
        int v, w, next;
    } edge[5000010];
    int n, a, b, c, cnt, head[50005], dis[50005], maxb;
    bool vis[50005];
    
    void addEdge(int u, int v, int w) {
        edge[++cnt].v = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    void spfa(int s) {
        queue<int> q;
        for (int i = 0; i <= maxb + 1; ++i) {
            dis[i] = -1;
        }
        dis[s] = 0;
        vis[s] = true;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            vis[u] = false;
            for (int i = head[u]; i; i = edge[i].next) {
                int v = edge[i].v;
                if (dis[v] < dis[u] + edge[i].w) {
                    dis[v] = dis[u] + edge[i].w;
                    if (!vis[v]) {
                        q.push(v);
                        vis[v] = true;
                    }
                }
            }
        }
    }
    
    int main() {
        io_speed_up;
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> a >> b >> c;
            addEdge(a, b + 1, c);
            maxb = maxb >= b ? maxb : b;
        }
        for (int i = 1; i <= maxb + 1; ++i) {
            addEdge(i - 1, i, 0);
            addEdge(i, i - 1, -1);
        }
        spfa(0);
        cout << dis[maxb + 1] << endl;
        return 0;
    }
    

    D - 飞行路线

    建立多层图,最多免费(k)次,所以有(k+1)层图,第(i)层表示当前已用(i)次免费机会,加边时在各层的都加上同一条边,一次免费相当于在上下两层的两点之间建立一条权值为(0)的边,起点在第(0)层,对整个多层图跑一遍最短路,一个点的最短距离就是其在各层中的最短距离的最小值。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    #define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
    #define INF 0x3f3f3f3f
    
    int head[110010], n, m, k, s, t, cnt;
    long long dis[110010];
    bool vis[110010];
    
    struct Edge {
        int v, next;
        long long w;
    } edge[11000010];
    
    void addEdge(int u, int v, long long w) {
        edge[++cnt].v = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    void spfa(int s) {
        queue<int> q;
        memset(vis, 0, sizeof(vis));
        for (int i = 0; i < k * n + n; ++i) {
            dis[i] = INF;
        }
        dis[s] = 0;
        vis[s] = true;
        q.push(s);
        while (!q.empty()) {
            int u = q.front();
            vis[u] = false;
            q.pop();
            for (int i = head[u]; i; i = edge[i].next) {
                int v = edge[i].v;
                if (dis[v] > dis[u] + edge[i].w) {
                    dis[v] = dis[u] + edge[i].w;
                    if (!vis[v]) {
                        q.push(v);
                        vis[v] = true;
                    }
                }
            }
        }
    }
    
    int main() {
        io_speed_up;
        cin >> n >> m >> k >> s >> t;
        for (int i = 1, u, v, w; i <= m; ++i) {
            cin >> u >> v >> w;
            for (int j = 0; j <= k; ++j) {
                int tu = u + n * j, tv = v + n * j;
                if (j != k) {
                    addEdge(tu, tv + n, 0);
                    addEdge(tv, tu + n, 0);
                }
                addEdge(tu, tv, w);
                addEdge(tv, tu, w);
            }
        }
        spfa(s);
        long long ans = INF;
        for (int i = 0; i <= k; ++i) {
            ans = min(ans, dis[t + n * i]);
        }
        cout << ans;
        return 0;
    }
    

    E - Watering Hole

    在已有图中添加一个点,表示地下天然水源,该点与其他各点之间建立一条权值为打井费用的边,在某一点打井相当于选择该点与地下水源之间的边,然后跑最小生成树即可。

    #include <algorithm>
    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    int n, fa[310], cnt;
    
    struct Edge {
        int u, v, w;
        bool operator<(const Edge &obj) const { return w < obj.w; }
    } edge[45310];
    
    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    
    int kruscal() {
        int ans = 0;
        for (int i = 0; i <= n; ++i) {
            fa[i] = i;
        }
        int tot = 0;
        for (int i = 1; i <= cnt; ++i) {
            int u = edge[i].u, v = edge[i].v;
            int fau = find(u), fav = find(v);
            if (fau != fav) {
                fa[fau] = fav;
                tot++;
                ans += edge[i].w;
                if (tot == n) {
                    return ans;
                }
            }
        }
    }
    
    int main() {
        while (scanf("%d", &n) == 1) {
            cnt = 0;
            for (int i = 1; i <= n; ++i) {
                edge[++cnt].u = 0;
                edge[cnt].v = i;
                scanf("%d", &edge[cnt].w);
            }
            for (int i = 1, w; i <= n; ++i) {
                for (int j = 1; j <= n; ++j) {
                    scanf("%d", &w);
                    if (i < j) {
                        edge[++cnt].u = i;
                        edge[cnt].v = j;
                        edge[cnt].w = w;
                    }
                }
            }
            sort(edge + 1, edge + cnt + 1);
            printf("%d
    ", kruscal());
        }
        return 0;
    }
    

    G - The Perfect Stall

    二分图匹配模板。

    #include <cstring>
    #include <iostream>
    using namespace std;
    #define io_speed_up ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
    
    int n, m, link[210], ans;
    bool vis[210], g[210][210];
    
    bool hungry(int now) {
        for (int i = 1; i <= n; i++) {
            if (!vis[i] && g[now][i]) {
                vis[i] = true;
                if (!link[i] || hungry(link[i])) {
                    link[i] = now;
                    return true;
                }
            }
        }
        return false;
    }
    
    int main() {
        io_speed_up;
        while (cin >> n >> m) {
            memset(g, 0, sizeof(g));
            memset(link, 0, sizeof(link));
            for (int i = 1, s, x; i <= n; ++i) {
                cin >> s;
                while (s--) {
                    cin >> x;
                    g[i][x] = true;
                }
            }
            ans = 0;
            for (int i = 1; i <= n; ++i) {
                memset(vis, 0, sizeof(vis));
                ans += hungry(i);
            }
            cout << ans << endl;
        }
        return 0;
    }
    

    H - COURSES

    还是二分图匹配模板。

    #include <cstring>
    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    int t, p, n, link[310], ans, cnt, head[310];
    bool vis[310];
    struct Edge {
        int v, next;
    } edge[30010];
    
    void addEdge(int u, int v) {
        edge[++cnt].v = v;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    bool hungry(int u) {
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (!vis[v]) {
                vis[v] = true;
                if (!link[v] || hungry(link[v])) {
                    link[v] = u;
                    return true;
                }
            }
        }
        return false;
    }
    
    int main() {
        scanf("%d", &t);
        while (t--) {
            scanf("%d%d", &p, &n);
            cnt = 0;
            memset(head, 0, sizeof(head));
            memset(link, 0, sizeof(link));
            for (int i = 1, k, x; i <= p; ++i) {
                scanf("%d", &k);
                while (k--) {
                    scanf("%d", &x);
                    addEdge(i, x);
                }
            }
            ans = 0;
            for (int i = 1; i <= p; ++i) {
                memset(vis, 0, sizeof(vis));
                ans += hungry(i);
            }
            if (ans == p) {
                puts("YES");
            } else {
                puts("NO");
            }
        }
        return 0;
    }
    
  • 相关阅读:
    POJ 1840 Eqs 二分+map/hash
    【vijos】P1514天才的记忆
    函数介绍
    函数参数和函数返回值
    函数参数和函数返回值
    前端项目里常见的十种报错及其解决办法
    前端项目里常见的十种报错及其解决办法
    BootstrapTable的使用教程
    BootstrapTable的使用教程
    前端js实现打印(导出)excel表格
  • 原文地址:https://www.cnblogs.com/IzumiSagiri/p/15017303.html
Copyright © 2011-2022 走看看