• [四连测(三)]篱笆


    题目描述

    农夫FJ的奶牛们有空旷恐惧症,所以,FJ打算在他的农场围上篱笆。他的农场是一个矩形区域。左上角的坐标是(0,0),右下角的坐标是(A,B),FJ修建了n(0<=n<=2000)个竖直的篱笆,其横坐标分别为a1,a2,a3,……,an,其中0<ai<A,,每一个篱笆从(ai,0)到(ai,B)也修建了m个水平的篱笆,其纵坐标为b1,b2,b3,……,bm,其中0<bi<B,每一个篱笆从(0,bi)到(A,bi)。这些篱笆把整个农场分成(n+1)*(m+1)个区域。

    不幸的是FJ忘了在篱笆上装门了。这导致奶牛无法在不同的区域之间移动。于是他决定将某些篱笆拆掉。现在要使得所有的区域都联通,请问最少要拆掉多长的篱笆。

    比如下面这个篱笆

    +---+--+

    |      |    |

    +---+--+

    |      |   |  

    |      |   |

    +---+--+

    可以这么拆:

    +---+--+

    |          |  

    +---+  +  

    |          |  

    |          |

    +---+--+

    输入

    第一题包含四个数A,B,n,m。(0<=A,B<=1000000000).

    接下来有两行,第二行n个数,表示a1,a2,……,an,表示竖直的n个篱笆的横坐标,第三行m个数,表示b1,b2,b3,……,bm,表示m个水平的篱笆的纵坐标。

    输出

    最少要拆除的篱笆的总长度。结果可能超过int。请用long long int

    样例输入

    15 15 5 2
    2
    5
    10
    6
    4
    11
    3

    样例输出

    44

    解题思路

    其实这道题仔细思考一下,或者画一下图,是可以很轻松地看出来,就是求一个最小生成树。

    把每一个方形看做一个点,进行编号,那么肯定是有点可以互相连通的。边权就是他们篱笆的长度,求个最小生成树即可。但这道题数据量有点大,而最小生成树的算法却是还要排序,这样一弄时间就炸了。于是我们就需要进行离散化。

    我们可以知道,边权相等的是有很多的,我们就把它们只算一下,这样进行排序,然后在计算。

    但我这次要说一个比较快速的方法。其实思路跟最小生成树差不多。

    我们依次删除最小长度的篱笆,处理好不多删,这样绝对是最优的,那该如何处理呢?我们先将横竖篱笆进行排序。首先,最小的横着的篱笆和竖着的篱笆是肯定都要建造的,这样是最优的。然后,就在横竖篱笆中选长度小的消除了。但做多次后,就会发现,有些点已经连通了,我们不用多删除这一篱笆,然而,这是否有规律呢?

    如图,删除箭头所指的这一段篱笆时,有一些已经通过前面的删除可以连通的,因此我们就可以少删一点,此图可以少删1个篱笆。而删除的横着的篱笆那一段刚好为2。没错,就是这样的规律。

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<iostream>
    #include<vector>
    #include<queue>
    #include<cstdlib>
    #include<algorithm>
    #define  N 2005
    using namespace std;
    int A,B,n,m , a[N] ,b[N];
    long long ans;
    int read(){
        int f = 1 , x = 0;
        char s = getchar ();
        while (s < '0' || s > '9'){
            if (s == '-')
                f = -1;
            s = getchar();
        }
        while (s >= '0' && s <= '9'){
            x = x * 10 + s - '0';
            s = getchar();
        }
        return x * f ;
    }
    int main(){
        A = read();
        B = read();
        n = read();
        m = read();
        for (int i = 1;i <= n ;i ++)
            a[i] = read();
        for (int i = 1 ;i <= m ;i ++)
            b[i] = read();
        a[++n] = A;//需要加上最后一条边,那一段篱笆需要计算
        b[++m] =B;
        sort(a+1,a+1+n);
        sort(b+1,b+1+m);
        for (int i = n ; i >= 1 ;i --)//计算篱笆长度,这里分开
            a[i] = a[i] - a[i - 1];
        for (int i = m ; i >= 1 ;i --)
            b[i] = b[i] - b[i - 1];
        sort(a+1,a+1+n);
        sort(b+1,b+1+m);
        ans += 1ll * a[1] * (m - 1) + 1ll * b[1] * (n - 1);//最小的横竖篱笆一定要。
        int cnta = 2 ,cntb = 2;
        while (cnta <= n &&cntb <= m){//这里依次枚举
            if (a[cnta] < b[cntb]){
                ans += (1ll * a[cnta] * (m - cntb + 1));
                cnta++;
            }
            else{
                ans += (1ll * b[cntb] * (n - cnta + 1));
                cntb ++;
            }
        }
        printf("%lld",ans);
    }
  • 相关阅读:
    前端CSS部分简单整理
    前端HTML部分简单整理
    Top Android App使用的组件
    使用DialogFragment创建对话框总结
    Rails常用命令
    developer.android.com笔记
    Google Maps API v2 Demo Tutorial
    Android学习的一些问题
    Android学习过程
    Beginning Android 4 Programming Book学习
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/13566696.html
Copyright © 2020-2023  润新知