离散优化初步

离散优化

我们先来看一道大水题(特别水和点击重新加载一样水)

线段覆盖
时间限制: 1 Sec 内存限制: 128 MB
X轴上方有若干条平行于X轴的线段,求这些线段能覆盖到的X轴的总长度?
输入
第一行一个数n(n<=1000),表示线段的个数;
接下来n行,每行两个整数ai,bi
(-10^8<=ai,bi<=10^8),代表一个线段的两个端点。
输出
输出覆盖x轴的长度。
样例输入
2
10 12
2 4
样例输出
4

乍一看这道题感觉水得很彻底,完全可以用sort对线段的左端点排一遍序,然后找右端点与下一条线段的左端点进行比较,如果大于就重复,小于就当前记录的右端点减去当前记录的左端点,重新记录。

其实这道题就是这么水。

当然还有另外一种做法,把每一个坐标开成一个bool数组的下标,遇到可以覆盖的就把覆盖的区域的值变为1,最后遍历一遍,就可求出最大的覆盖长度。那么问题来了,在128M的内存限制下开一个如此之大的当然是可以的,但如果数据更大,达到了10^10怎么办呢,这个时候就需要用一波另外的玄学方法。

这就是今天说的另外一种做法(终于说道正题了)——离散优化。

什么是离散优化呢?

离散优化就是在数据大小很大,但是数据个数很小的情况下使用的的一种神奇的算法。为什么说它神奇呢,以这道题为例。

我们把线段对应的端点两两之间分成一段,然后这有什么用呢?用处就大了,我们只需要建立一个大小为2*n的数组(a[ ])就可以进行操作了,再通过一个flag数组记录端点与端点之间的关系(flag[i]=1表示a[i]到a[i+1]被覆盖了)。
首先我们把每一个端点都存入数组中,然后用sort对端点数组进行排序,排好之后再对每条线段的左端点和右端点进行查找,找到之后,把flag数组中的左端点位置到右端点位置都赋值为1。

具体语句见代码。

#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
    int left,right;
}edge[1100];
int a[2500];
bool flag[2500];
int half(int x,int l,int r) //二分查找可以提高时间效率
{
    if(l>r) return -1;
    if(x==a[(l+r)/2]) return (l+r)/2;
    if(x>a[(l+r)/2]) half(x,(l+r)/2+1,r);
    else if(x<a[(l+r)/2]) half(x,l,(l+r)/2-1);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&edge[i].left,&edge[i].right);
        a[2*i-1]=edge[i].left;
        a[2*i]=edge[i].right;
    }
    sort(a+1,a+2*n+1);
    for(int i=1;i<=n;i++)
    {
        int l=half(edge[i].left,1,2*n);
        int r=half(edge[i].right,1,2*n);
        for(int j=l;j<r;j++)
            flag[j]=true;
    }
    int ans=0;
    for(int i=1;i<=2*n;i++)
        if(flag[i])
            ans+=a[i+1]-a[i];
    printf("%d",ans);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值