zoukankan      html  css  js  c++  java
  • 最小生成树——垃圾佬抓宠物

    博客借鉴:https://blog.csdn.net/qq_36553623/article/details/78524754

    题目:

    垃圾佬抓宠物
    TimeLimit:1000MS  MemoryLimit:128MB
    64-bit integer IO format:%lld
    
    Problem Description
    垃圾佬也有头痛的时候,这不,垃圾佬的GF又缠着他了……
    
    静静:“Darling!”
    垃圾佬:“怎么?”
    静静:“我玩那个OWO更新了哦~新增的宠物系统好可爱啊~不过抓宠物要用好多魔法豆哎”
    垃圾佬:“那个魔法豆是打怪得的?”
    静静:"不是哦,用RMB 买才得。"
    垃圾佬:“哎,现在的网游,就是会骗钱。”
    静静:“怎么能这么说人家呢,那些宠物都好可爱的,花点钱算什么嘛。我打算把所有的宠物都抓到哦~”
    垃圾佬:“又说没有魔法豆,难道……”
    静静:“我把你的钱都拿来冲成魔法豆了哦~你看你看,这些宠物都好可爱的,你不会有意见吧?^_^”
    垃圾佬:“……没有。”
    静静:“不要苦瓜着脸嘛,要不这样吧,你帮我把所有的宠物都抓起来,剩下的魔法豆我再帮你换回RMB 好不好?^_^”
    垃圾佬:“……好……好吧”
    
    这下惨了,垃圾佬现在好后悔在静静面前炫耀自己的钱包啊……
    静静做事,向来说一不二,抓齐宠物恐怕是免不了的了。
    不过,垃圾佬发现,这个网游的抓宠物系统是这样设定的:
    抓特定的某种宠物需要花费特定的魔法豆。
    另外,还可以通过一种宠物的呼唤能力来抓取另一种宠物,当然,要让宠物发挥它的呼唤能力需要两个条件:
        第一,它要是你的宠物……人家还野生呢总不会听你的话陷害好友吧。
        第二,你需要喂给你的宠物一定的魔法豆,它才有力气施展它的能力。
    当另一只宠物被呼唤过来之后,你不费什么力气就可以把它抓住,也就不必再向系统付额外的魔法豆了。
    注意,一种宠物能呼唤另一种宠物是因为他们心灵相通,所以如果A宠物能呼唤B宠物,那么B宠物也一定能呼唤A宠物。
    而且,垃圾佬仔细研究之后发现,可能是OWO的程序员懒得再设置数据,A 宠物呼唤B宠物需要的魔法豆和B宠物呼唤A宠物需要的魔法豆是相等的。
    垃圾佬的目标,当然是要算出最少要花费多少魔法豆啦~
    垃圾佬拿出纸笔,算啊算啊算啊算,就在垃圾佬算出来的一刹那,静静温柔的对垃圾佬说:“Darling,你会不会觉得我很败家啊……其实我也知道我不应该用你的钱来冲魔法豆的”
    垃圾佬心中一喜,抬头看着静静,静静说:“要不你不用抓齐,少抓一只吧。”
    垃圾佬呆住了……这是一个多么体贴的GF啊!许久许久,垃圾佬又拿起纸笔重新算过。
    Input
    第一行一个数字n,代表网游里一共有n 种宠物。
    第二行有n 个数字,用空格隔开,依次说明直接抓取第1,234……n 种宠物需要多
    少魔法豆
    第三行一个C,表示游戏一共设定了C 对宠物心灵相通,可以互相呼唤。
    接下来C 行,每行都有三个数字a,b 和w。代表第a 种和第b 种宠物可以互相呼唤,
    呼唤前需要给a 宠物或b 宠物喂w 魔法豆。
    
    n≤250,n∈N+
    0<=c<=n*(n-1)/2
    所有数字(含结果)<2^31
    
    Output
    只有一行,满足MM 需求最少要花费多少魔法
    SampleInput
    3
    10 10 10
    3
    1 2 7
    1 3 1
    2 3 3
    SampleOutput
    11
    
    样例说明:先抓第一只,然后用第一只呼唤第三只。
    View Code

    题目大意:给你 n 个点,m 条边,其中选择任意一点需要消耗一定代价,选择效果为点亮这一点,

    选择任意一条边也需要消耗一定代价,选择前提为此边顶点有一点已经点亮,选择效果为点亮另外一个顶点,

    问:如何以最小代价点亮 n-1 个点。

    一,添加点 与 删除点

    添加点:将 点亮一个点 看成是图外一点 连接这一点,这操作太秀了。

    原先点 是从 1~n,这样再设一点 0 就可以了。

     删除点:由于题目要求可以不要 一点,这一点可以任意选择,因为 n 比较小,

    于是直接暴力枚举 1~n 的每个点。

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define N (int)1e5+10
    #define MIN(x,y) (x<y?x:y)
    int n, m, sum;
    int p[N], a[N];
    struct edge
    {
        int x, y, w;
    }e[N];
    int cmp(const edge&a, const edge&b)
    {
        return a.w < b.w;
    }
    int find(int x)
    {
        if (p[x] != 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 j = 0; j <= n; j++)  // 添加一个  结点 0
            p[j] = j;
        sum = 0;
    }
    int main(void)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        scanf("%d", &m);
        for (int i = 1; i <= m; i++)
            scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].w);
        for (int i = 1; i <= n; i++)
        {
            e[i + m].x = 0;
            e[i + m].y = i;
            e[i + m].w = a[i];
        }
        int ans = ~0U >> 1;  // 最大值
        sort(e + 1, e + m + n + 1, cmp);
        for (int i = 1; i <= n; i++)
        {
            init();
            for (int j = 1; j <= m + n; j++)
            {
                if (e[j].x == i || e[j].y == i)   //删除 1~n 中的一个节点
                    continue;
                if (join(e[j].x, e[j].y))
                    sum += e[j].w;
            }
            ans = MIN(ans, sum);
        }
        printf("%d
    ", ans);
    
        system("pause");
        return 0;
    }
    View Code

    二,错误代码

    ① 刚开始没注意看题,以为 选择点 一定比 选择边 代价少,于是将 选择边 和 选择点 分开计算,默认优先 选择点

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    using namespace std;
    #define N 255
    int p[N], b[N];
    struct edge
    {
        int from, to, w;
    }e[90000];
    struct node
    {
        int w, vis;
    }a[N];
    int cmp(const edge& a, const edge& b)
    {
        return a.w < b.w;
    }
    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;
    }
    int main(void)
    {
        int n, nn = 2147483647;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i].w);
            if (a[i].w < nn)
                nn = a[i].w;
            p[i] = i;
        }
        int m; scanf("%d", &m);
        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);
        int f = 0;
        if (n - 2 <= m)
        {
            m = n - 2;
            f = 1;
        }
        else
            m = n - 1;
        int sum = 0, t = 0;
        for (int i = 1; i <= m; i++)
        {
            if (join(e[i].from, e[i].to))
            {
                sum += e[i].w;
                a[e[i].from].vis = a[e[i].to].vis = 1;
            }
        }
        if (f)
        {
            printf("%d
    ", sum + nn);
            system("pause");
            return 0;
        }
        int bb = 0; nn = 2147483647;
        for (int i = 1; i <= n; i++)
        {
            if (a[i].vis == 0)
                b[++bb] = a[i].w;
            if (a[i].vis == 1)
                if (a[i].w < nn)
                    nn = a[i].w;
        }
        sort(b + 1, b + bb + 1);
        for (int i = 1; i <= bb - 1; i++)
        {
            sum += b[i];
        }
        printf("%d
    ", sum + nn);
    
        system("pause");
        return 0;
    }
    /*
    4
    1 2 3 4
    1
    2 3 1
    3
    
    5
    1 2 3 4 5
    2
    1 2 1
    1 3 1
    
    5
    1 2 3 4 5
    6
    1 2 1
    1 3 1
    1 4 1
    1 5 1
    2 3 2
    3 4 3
    */
    View Code

    ② 在删除点的时候,没有选择枚举,而是直接选出 n-2 条边,最后第 n-1 条边再特殊判断一下,保证 0 点一定选中。想不出来为什么不可以。(╥╯^╰╥)

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define N (int)1e5+10
    #define MIN(x,y) (x<y?x:y)
    int n, m, sum;
    int p[N], a[N], vis[N];
    struct edge
    {
        int x, y, w;
    }e[N];
    int cmp(const edge&a, const edge&b)
    {
        return a.w < b.w;
    }
    int find(int x)
    {
        if (p[x] != 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 j = 0; j <= n; j++)  // 添加一个  结点 0
            p[j] = j;
        sum = 0;
    }
    int main(void)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        scanf("%d", &m);
        for (int i = 1; i <= m; i++)
            scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].w);
        for (int i = 1; i <= n; i++)
        {
            e[i + m].x = 0;
            e[i + m].y = i;
            e[i + m].w = a[i];
        }
        int ans = ~0U >> 1;  // 最大值
        sort(e + 1, e + m + n + 1, cmp);
    
        init();
        int f = 0, t = 0;
        for (int i = 1; i <= m + n; i++)
        {
            if (t == n - 1)
                break;
            if (t == n - 2)
            {
                if (join(e[i].x, e[i].y))
                {
                    if (f == 0 && e[i].x == 0 && vis[e[i].y] == 1)   // f == 0 还没有添加 0 点
                        sum += e[i].w, t++;
                    if (f == 1 && vis[e[i].x] == 1 && vis[e[i].y] == 1)   // f == 1 已经添加 0 点
                        sum += e[i].w, t++;
                }
            }
            if (t != n - 2)
                if (join(e[i].x, e[i].y))
                {
                    vis[e[i].x] = vis[e[i].y] = 1;
                    sum += e[i].w;
                    t++;
                    if (e[i].x == 0)
                        f = 1;     
                }
        }
        printf("%d
    ", sum);
    
        system("pause");
        return 0;
    }
    View Code

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

          题鹤林寺僧舍    (唐 李涉 )

    终日昏昏醉梦间,忽闻春尽强登山。
    因过竹院逢僧话,偷得浮生半日闲。
  • 相关阅读:
    771. Jewels and Stones
    706. Design HashMap
    811. Subdomain Visit Count
    733. Flood Fill
    117. Populating Next Right Pointers in Each Node II
    250. Count Univalue Subtrees
    94. Binary Tree Inorder Traversal
    116. Populating Next Right Pointers in Each Node
    285. Inorder Successor in BST
    292. Nim Game Java Solutin
  • 原文地址:https://www.cnblogs.com/asdfknjhu/p/13280545.html
Copyright © 2011-2022 走看看