[四连测(三)]篱笆

题目描述

农夫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);
}
原文地址:https://www.cnblogs.com/lover-fucker/p/13566696.html