关于 海平面上升 与 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

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

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

天尽头,何处有香丘?

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

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

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

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

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

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

原文地址:https://www.cnblogs.com/asdfknjhu/p/13118052.html