• 关于 海平面上升 与 fold的毒瘤题(easy) 的思考


    海平面上升:http://www.fjutacm.com/Contest.jsp?cid=875#P0

    fold的毒瘤题(easy): http://www.fjutacm.com/Contest.jsp?cid=869#P8

    这两道题既有相似的地方,又有不同的地方。总之,就是方法多了有点乱,特意研究一下。

    一,海平面上升

    {  floyd   直接枚举所有路径的情况,然后可以直接查询 能满足两点连接的最高高度

    { kruskal  用生成树的方法 直接连接所有满足 条件的边,然后看一下所求的两个点是否连接

    二,fold的毒瘤题

    { floyd  直接枚举所有路径的情况,然后求出 特殊点间的最大路径

    { kruskal  因为题目保证特殊点之间一定可以连接,所以不用判断这些点是否连通。但是,题目要求我们记录边权最大值最小的哪个方案,可以注意到,我们使用 kruskal 的过程中,选的边的权值是递增的,所以当我们在这些特殊 点连通之后,立刻退出,则此刻连通这些特殊点的边 即为边权最大值最小的那个方案,且添加的最后一条边满足:①必在特殊点的连通路径上 ② 连接边上最大的一条边,所以这条边就是答案。

    { prim  严格来说原理和 kruskal 一样,只是实现方法不一样,所以这里基本和上面一样,只是 退出条件变了,

    上面是在这些特殊 点连通之后,prim 是这些特殊点全部并入 生成树的点集之时。

    三,思考

    1,两道题目都可以看成求同一个东西。第一题是:求边权最小值最大的那条边,第二题是:求边权最大值最小的那条边

    因为只记录一个数据,且限制条件都是和边有关的,所以都可以直接用 floyd 暴力任意两点间的所有路的情况,并记录数据。          

     2,为什么 第二题可以用 prim,第二题 可以呢?

    注意 kruskal 和 prim 在实现方法的不同,一个是依赖于边,一个是依赖于点。

    上这两题都给出了边的限制条件,但第一题却没有给出点的,所以不建议用 prim

    这里说明一下,第一题 边的限制条件:桥的高度比海平面高,才能通过。而点的限制条件,需要我们先求出 满足条件的边,在求出这些边的端点,十分麻烦。

     而第二题 没有给出边的限制条件,却给出了点的限制条件,但 kruskal 的并查集也是可以用点进行判断的,所以两种方法都可以。

    3,第二题只要求我们连接这些特殊点,但是我们却直接用 最小生成树的方法,不会有影响吗?

    不错,第二题我们在最后停止后,生成的树,是会出现割边的 ,且那个度数为 1 的点不是特殊点,这些边没有贡献于特殊点的连接。

    其实这个可以用反证法,①,这些无用割边绝不会是连通的最后一条边,所以不会被选中

    ②,我们要求的不是 连接所有特殊点的所有路径,而是 求边权最大值最小的那个方案,只要连接的每一条边都是最小的就好,且你是刚刚好连通就退出。

    假设还有一个方案的边权最大值比我们求出来的还小,则这样你的其他方案,必然至少要添加此刻这颗不完全的生成树没有的边,

    而这条边必然比这里所有的边都大,也就无法比我们求出来的还小了,这就矛盾了。

    四,代码

    第一题 floyd   

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #define MAX(x,y) ((x)>(y))?(x):(y)
    #define MIN(x,y) ((x)<(y))?(x):(y)
    #define N 205
    #define inf 0x3f3f3f3f   // 10 位数
    int dp[N][N];  // 刚开始存的是边的距离,到后面就是 i 到 j 的所有路中,恰好能保证通过,的桥的最低距离,即当条路最低,所有路最高 的桥的高度
    int n, m, q;
    void init()
    {
        memset(dp, 0, sizeof(dp));  // 0 表示 i 到 j 没有直达路径
        // 因为多条路径 要求最大值,所以要初始化 为 0,可以保证无路的这条边 不会被选中 
    }
    void floyd()
    {
        for (int k = 1; k <= n; k++)
        {
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= n; j++)
                {
                //    if (dp[i][k] && dp[k][j])  // 列举 i 到 j 的某条路径
                        dp[i][j] = MAX(dp[i][j], MIN(dp[i][k], dp[k][j]));
                    // MIN(dp[i][k], dp[k][j]) 同一条路径中  其决定性的是最短的    短板原理
                    // MAX(dp[i][j], MIN(dp[i][k], dp[k][j])) 在所有路径中决定可达能否的因素是:最高的桥的高度 
                } 
            }
        }
    }
    int main(void)
    {
        init();
        scanf("%d%d%d", &n, &m, &q);
        for (int i = 1; i <= m; i++)
        {
            int a, b, h; scanf("%d%d%d", &a, &b, &h);
            dp[a][b] = dp[b][a] = MAX(dp[a][b], h);
        }
        floyd();
        while (q--)
        {
            int x, y, h; scanf("%d%d%d", &x, &y, &h);
            if (dp[x][y]>h)
                puts("G0");
            else
                puts("PlTY");
        }
    
        system("pause");
        return 0;
    }
    View Code

    第一题 kruskal

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    #define MAX(x,y) (x>y?x:y)
    using namespace std;
    #define N 20005
    struct edge
    {
        int  from, to, w;
    }e[N];
    int cmp(edge& a, edge& b)
    {
        return a.w > b.w;  // 要先选桥高的桥,这样才能尽可能 通过
    }
    int p[N], n, m, q;
    int find(int x)
    {
        if (x != p[x])
            p[x] = find(p[x]);
        return  p[x];
    }
    int join(int x, int y)
    {
        x = find(x), y = find(y);
        if (x == y)
            return 0;
        p[x] = y;
        return 1;
    }
    void init()
    {
        for (int i = 0; i <= n; i++)
            p[i]=i;
    }
    int main(void)
    {
        scanf("%d%d%d", &n, &m, &q);
        for (int i = 1; i <= m; i++)
            scanf("%d%d%d", &e[i].from, &e[i].to, &e[i].w);
        sort(e + 1, e + m + 1, cmp);
        while (q--)
        {
            init();
            int x, y, h; scanf("%d%d%d", &x, &y, &h);
            int sum = 0, f = 1;
            for (int i = 1; i <= m&&e[i].w>h; i++)
            {
                join(e[i].from, e[i].to);   // 将所有高于 海平的桥 连接在一起,看一下 是否能让 x,y 连通
                if (find(x) == find(y))
                {
                    f = 0;
                    break; 
                }
            }
            if (f == 0)
                puts("G0");
            else
                puts("PlTY");
        }
    
        system("pause");
        return 0;
    }
    View Code

    第二题 floyd

    #define  _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    #include<string.h>
    #define MAX(x,y) (x>y?x:y)
    #define MIN(x,y) (x<y?x:y)
    #define inf 0x3f3f3f3f
    #define N 505
    int dp[N][N], n, m, q;
    int a[N];  // 存特殊点的编号的
    using namespace std;
    void init()
    {
        memset(dp, 0x3f, sizeof(dp));   // 因为保证 任意两点 之间 必有路,且最后 找的是最大值中的最小值,初始化为 inf,才不会被选中
    }
    void folyd()
    {
        for (int k = 1; k <= n; k++)
        {
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= n; j++)
                {
                    //if (dp[i][k] && dp[k][j])    因为保证 任意两点 之间 必有路,所以不用判断
                        dp[i][j] = MIN(dp[i][j], MAX(dp[i][k], dp[k][j]));
                    //   MAX(dp[i][k], dp[k][j])   单条路径 找最大值
                    //   MIN(dp[i][j], MAX(dp[i][k], dp[k][j]))   多条路径 找最大值的最小值
                }
            }
        }
    }
    int main(void)
    {
        scanf("%d%d%d", &n, &m, &q);
        init();
        for (int i = 0; i < m; i++)
        {
            int a, b, w; scanf("%d%d%d", &a, &b, &w);
            dp[a][b] = dp[b][a] = MIN(dp[a][b], w);   // 让同一条路径上的 边权最大值 尽可能小
        }
    
        folyd();
        while (q--)
        {
            int L, R, P, C; scanf("%d%d%d%d", &L, &R, &P, &C);
            int sum = 0, tot = -1;
            for (int i = L; i <= R; i++)
                if (i%P == C)
                    a[++tot] = i;
    
            for (int i = 0; i <= tot; i++)   // 找到这些特殊点中的 边权最大值
            {
                for (int j = i + 1; j <= tot; j++)
                {
                    sum = MAX(dp[a[i]][a[j]], sum);
                }
            }
            printf("%d
    ", sum);
        }
        system("pause"); 
        return 0;
    }
    View Code

    第二题 kruskal

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    using namespace std;
    #define  MAX(x,y) (x>y)?(x):(y)
    #define N 505
    struct edge
    {
        int from, to, w;
    }e[N];
    int cmp(edge& a, edge& b)
    {
        return a.w < b.w;   // 单条路每一条边尽可能小,才能让 这条路的最大值 最小
    }
    int p[N], n, m, q;
    int a[N];
    int find(int x)
    {
        if (x != p[x])
            p[x] = find(p[x]);
        return p[x];
    }
    int join(int x, int y)
    {
        x = find(x), y = find(y);
        if (x == y)
            return 0;
        p[x] = y;
        return 1;
    }
    void init()
    {
        for (int i = 1; i <= n; i++)
            p[i] = i, a[i] = 0;
    
    }
    int main(void)
    {
        scanf("%d%d%d", &n, &m, &q);
        for (int i = 1; i <= m; i++)
            scanf("%d%d%d", &e[i].from, &e[i].to, &e[i].w);
        sort(e + 1, e + m + 1, cmp);
        while (q--)
        {
            init();
            int L, R, P, C; scanf("%d%d%d%d", &L, &R, &P, &C); 
            int sum = 0, tot = -1;
            for (int i = L; i <= R; i++)
                if (i%P == C)
                    a[++tot] = i;
            for (int i = 1; i <= m; i++)
            {
                join(e[i].from, e[i].to);
                sum = e[i].w;     // 都选的边一定是生成子图中最大的,所以停止的时候, sum 的值一定是在 子图的生成树的路径上
    
                int f = 0;
                for (int j = 0; j < tot; j++)   
                    if (find(a[j]) != find(a[j + 1]))
                        f = 1;
                if (f == 0)
                    break;
            }
            printf("%d
    ", sum);
        }
    
        system("pause");
        return 0;
    }
    View Code

    第二题 prim

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #define MAX(x,y) (x>y?x:y)
    #define MIN(x,y) (x<y?x:y)
    #define N 55
    #define inf 0x3f3f3f3f-1
    int a[N][N], vis[N], dis[N], sc[N];
    int n, m, q;
    int sum, s;
    void prim()
    {
        memset(vis, 0, sizeof(vis));
        for (int i = 1; i <= n; i++)
            dis[i] = inf;
        vis[s] = 1, sc[s] = 0;
        int ans = 0;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (vis[j] == 0 && dis[j] > a[s][j])
                    dis[j] = a[s][j];
            }
            int min = inf;
            for (int j = 1; j <= n; j++)
            {
                if (vis[j] == 0&&dis[j] < min)
                {
                    min = dis[j];
                    s = j;
                }
            }
            if (min == inf)
                return;
            vis[s] = 1;
            if (sc[s])
                sum--, sc[s] = 0;
            ans = MAX(ans, min);
            if (sum==1)
                break;
        }
        printf("%d
    ", ans);
    }
    int main(void)
    {
        scanf("%d%d%d", &n, &m, &q);
        memset(a, 0x3f, sizeof(a));
    
        for (int i = 1; i <= m; i++)
        {
            int u, v, w; scanf("%d%d%d", &u,&v,&w);
            a[v][u] = a[u][v] = MIN(a[u][v], w);
        }
        while (q--)
        {
            memset(sc, 0, sizeof(sc));
            int L, R, P, C; scanf("%d%d%d%d", &L, &R, &C, &P);
            sum = 0;
            for (int i = L; i <= R; i++)
            {
                if (i%C == P)
                {
                    s = i;    // 起点要在特殊点
                    sc[i] = 1;
                    sum++;   // 点数
                }
            }
            prim();
        }
        system("pause");
        return 0;
    }
    View Code

    ============ ========== ======== ======= ======= ===== ==== === == =

    葬花吟   (曹雪芹)林黛玉    清

    天尽头,何处有香丘?

    未若锦囊收艳骨,一抔净土掩风流。

    质本洁来还洁去,强于污淖陷渠沟。

    尔今死去侬收葬,未卜侬身何日丧?

    侬今葬花人笑痴,他年葬侬知是谁?

    试看春残花渐落,便是红颜老死时。

    一朝春尽红颜老,花落人亡两不知!

  • 相关阅读:
    boost::asio 连接管理11 如何关闭连接
    boost::asio async_write也不能保证一次发完所有数据 二
    boost::asio async_write也不能保证一次发完所有数据 一
    boost参考博客
    C++ 多线程编程总结
    Boost::asio io_service 实现分析
    使用boost io_service时,需要注意的东西
    Boost::Thread 多线程的基础知识
    boost::thread类
    Boost::thread库的使用
  • 原文地址:https://www.cnblogs.com/asdfknjhu/p/13118052.html
Copyright © 2020-2023  润新知