• Gift


    有一个国家有N个城市和M条道路,这些道路可能连接相同的城市,也有可能两个城市之间有多条道路。 有一天,有一伙强盗占领了这个国家的所有的道路。他们要求国王献给他们礼物,进而根据礼物的多少而放弃占领某些边。对于每一条道路,强盗都给出了相应的要求,金子gi的数量,银子si的数量。也就是说若国王给强盗G个金子,S个银子,那么他们就会放弃占领满足gi<=G and si<=S 的道路。 现在国王知道金子、银子的单价,他想花费钱财购买金银送给强盗,使强盗放弃一些道路,进而使N个城市能互相到达。但是国王又想花费最少。请你计算最少的花费。 

    Input

    第一行有两个整数N和M,表示有N个城市M条道路。 
    第二行有两个整数G和S,表示购买金子和银子的价格。 
    以后M行,每行4整数X,Y,g,s,表示这条道路连接X城市和Y城市,要求g个金子,s个银子。
    100% N<=200,M<=50000

    Output

    一个整数,表示最少花费。要是没有满足的情况,输出-1。

    Sample Input

    3 3

    2 1

    1 2 10 15

    1 2 4 20

    1 3 5 1

    Sample Output

    30

    sol:
    题目大概是说,从一堆边里面选出若干条,使得整个图联通,且要求选出的边中最大的g和s最小。
    最小生成树问题。

    把所有的边按照g值升序排序,然后从前往后枚举,每次枚举到一个新的值就把所有能用的边全部放到另外一个数组里,然后(kruskal)再把这个数组按照s值升序排序,做一遍最小生成树,直到符合条件为止。 

    几个优化: 

    首先,在我们加入一条新的边的时候,其实没必要用sort扫一遍,可以把它当成一个栈,从栈顶往下扫,如果新加入的这条边下面那条边的s值大于新加入的这条边的s值,那么就把他们的位置互换。

    然后,在过程中我们发现有一些边如果这次没用到,那么接下来也不可能会用到,所以就没有必要保留,直接舍弃就好,还能节约空间。也就是说,我们把每次选中的边都放到数组的最前端,加一个指针就好。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    struct edge {
        int x, y;
        long long g, s;
    } e[50010];
     
    int n, m;
    long long g, s;
    int st[210], top, cnt;
    long long ans = 0x7fffffffffffffffll;
    int fa[210];
     
    inline long long read() {
        long long x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
        while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
        return x * f;
    }
     
    void init() 
    {
        for (int i = 1; i <= n; i++) fa[i] = i;
    }
     
    bool cmp_g(edge a, edge b) 
    {
        if (a.g == b.g) return a.s < b.s;//金子数量相同按银子从小到大 
        return a.g < b.g;
    }
     
    int find(int x) 
    {
        if (x == fa[x]) return x;
        return fa[x] = find(fa[x]);
    }
     
    int main() {
    //  freopen("data.in", "r", stdin);
        n = read(), m = read(), g = read(), s = read();
        for (int i = 1; i <= m; i++) 
        {
        e[i].x = read(), e[i].y = read(), e[i].g = read(), e[i].s = read();
        }
        sort(e + 1, e + m + 1, cmp_g); //按金子的数量,从小到大 
        for (int i = 1; i <= m; i++) 
        {
        init();//初始化,并查集清空 ,因为每加一条边就要求一次最小生成树 
        //维护一个栈,栈里是组成最小生成树的边 
        st[++top] = i, cnt = 0;//加入一条新边 
        //对于新边,所需金子数量增加,我们当然希望银子数量减少才好
            for (int j = top; j >= 2; j--) 
            {
            if (e[st[j]].s < e[st[j - 1]].s) 
            {
            swap(st[j], st[j - 1]);
            }//扫栈 ,所需金子数量确定的情况下,银子所需数越小的越往栈底走 
            }
        //kruskal,每加一条边,就做一次mst 
        for (int j = 1; j <= top; j++) 
        {
            int eu = find(e[st[j]].x), ev = find(e[st[j]].y);
            if (eu == ev) continue;
            fa[ev] = eu;
            st[++cnt] = st[j];//将用到的边放到st数组中 
            if (cnt == n - 1) break;
        }
        if (cnt == n - 1) 
        {    //cout<<e[i].g<<"    "<<e[st[cnt]].s<<endl;
             ans = min(ans, e[i].g * g + e[st[cnt]].s * s);
             //更新答案
           
        }
        top = cnt;
        }
        if (ans == 0x7fffffffffffffffll) puts("-1");
        else printf("%lld", ans);
        return 0;
    }
  • 相关阅读:
    绿色版 notepad++ 添加鼠标右键菜单
    Scala 安装与配置
    Scala 神奇的下划线 _
    Kafka 安装部署
    Pulsar 下一代消息平台
    Sqoop 安装部署
    Flume 常用配置项
    Android-selector
    android- 9patch
    有关内存的思考题
  • 原文地址:https://www.cnblogs.com/cutepota/p/13453941.html
Copyright © 2020-2023  润新知