几类区间覆盖

这篇博客探讨了区间覆盖问题的解决策略,通过贪心算法实现整数和实数区间覆盖。首先介绍了如何处理整数区间覆盖,然后详细阐述了实数区间覆盖,特别是面对圆覆盖矩形的特殊情况。最后,通过一个实际问题展示了如何将区间覆盖转化为整数区间覆盖模板,并应用到城市与沙漠城市的连通性分析中。

区间覆盖:给定区间,和n的线段,要求选择最少的线段使给定区间完全覆盖。

做法:贪心,先根据左端点从小到大排序,先确定一个答案区间,然后在没有选择的线段中选择一个左端点在答案区间中,右端点最大的加入答案区间。

poj-2376

模板题,但这题是整数覆盖,所以有一个地方不同,具体见代码。

#include<iostream>
#include<algorithm>
using namespace std;

const int N = 25010;

int n,t;

struct stu
{
    int l,r;
} s[N];

bool cmp(stu q,stu e)
{
    if(q.l != e.l)
        return q.l < e.l;
    else return q.r > e.r;
}

int main()
{
    cin>>n>>t;
    for(int i = 0; i < n; i ++ )
        cin>>s[i].l>>s[i].r;

    int st = 1,en = t;

    sort(s,s + n,cmp);//按第一个从小到大排序

    int res = 0;
    bool flag = false;

    for(int i = 0; i < n; i ++ )
    {
        int j = i,maxx = 0;//j枚举左端点可以覆盖过去的点,maxx取他们右端点最大值

        while(j < n && s[j].l <= st)
        {
            maxx = max(maxx,s[j].r);
            j ++ ;
        }

        if(maxx < st)//最大值都没有覆盖到
            break;

        res ++ ;

        if(maxx >= en)//最大值大于终点
        {
            flag = true;
            break;
        }


        st = maxx + 1;//和线段的区间不一样的地方,点不带两点之间的线段
                      //如果是线段不需要加一
        i = j - 1;
    }

    if(flag)
        cout<<res;
    else
        cout<<-1;

    return 0;
}

UVA - 10382

实数区间覆盖模板,但这题有点麻烦,首先是很多数都需要取 double,然后由于是用圆去覆盖一个矩形,所以需要将圆与矩阵的两个交点求出来,分别为他能覆盖的区间的左右端点。

#include<bits/stdc++.h>
using namespace std;

const int N = 10010;

struct stu

{
    double l,r;
}s[N];

bool cmp(stu q,stu e)
{
    return q.l < e.l;
}

int n;
double l,w;

int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>l>>w)
    {
        int cnt = 0;//很重要!!!!在这wa了十几次!!
        for(int i = 0; i < n; i ++ )
        {
            double x,d;//用double
            cin>>x>>d;
            if(d > w / 2)//如果没有交点,不能把两个0,0放进去
            {
                s[cnt].l = x - (double)sqrt(1.0*d*d-1.0*w*w/4);
                s[cnt].r = x + (double)sqrt(1.0*d*d-1.0*w*w/4);
                cnt ++;
            }
        }

        sort(s,s + cnt,cmp);//数据的数量是cnt不是n!!
        bool flag = false;

        int res = 0;
        double st = 0,en = l;
        if(s[0].l > 0)
            cout<<"-1"<<endl;
        else
        {
            for(int i = 0; i < cnt; i ++ )
            {
                int j = i;
                double maxx = -1;
                while(j < cnt && s[j].l <= st)
                {
                    maxx = max(maxx,s[j].r);
                    j ++ ;
                }

                if(maxx < st)
                    break;

                res ++;
                if(maxx >= en)
                {
                    flag = true;
                    break;
                }

                st = maxx;//实数不用加一
                i = j - 1;
            }
            if(flag)
                cout<<res<<endl;
            else
                cout<<"-1"<<endl;
        }
    }
    return 0;
}

P1514

用 dfs 求出每个靠湖城市能够到的沙漠城市的区间,这里能到的沙漠城市一定是一个连续的区间,具体证明可看 证明 简单易懂,然后就转化成整数区间覆盖模板了。

#include<bits/stdc++.h>
using namespace std;

const int N = 510;

struct stu
{
    int l,r;
}s[N];

bool cmp(stu q,stu e)
{
    return q.l < e.l;
}

int n,m,res,g[N][N],l[N][N],r[N][N],v[N][N];
int dx[4] = {-1,0,0,1};
int dy[4] = {0,-1,1,0};

void dfs(int x,int y)
{
    v[x][y] = 1;
    for(int i = 0; i < 4; i ++ )
    {
        int nx = x + dx[i];
        int ny = y + dy[i];
        if(nx >= 1 && nx <= n && ny >= 1 && ny <= m && g[nx][ny] < g[x][y])
        {
            if(!v[nx][ny])
                dfs(nx,ny);
            l[x][y] = min(l[x][y],l[nx][ny]);
            r[x][y] = max(r[x][y],r[nx][ny]);
        }
    }
}

int main()
{
    cin>>n>>m;
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ )
            cin>>g[i][j];

    memset(l,0x3f,sizeof(l));//这个初始化初始化很关键
    for(int j = 1; j <= m; j ++ )
        l[n][j] = r[n][j] = j;

    for(int j = 1; j <= m; j ++ )
        dfs(1,j);

    for(int j = 1; j <= m; j ++ )
        if(!v[n][j])
            res ++ ;

    if(res)
        cout<<0<<endl<<res;
    else
    {
        cout<<1<<endl;
        for(int j = 1; j <= m; j ++ )
        {
            s[j].l = l[1][j];
            s[j].r = r[1][j];
        }

        sort(s + 1,s + m + 1,cmp);

        int st = 1,en = m;
        for(int i = 1; i <= m; i ++ )
        {
            int j = i,maxx = 0;
            while(j <= m && s[j].l <= st)
            {
                maxx = max(maxx,s[j].r);
                j ++ ;
            }

            res ++ ;

            if(maxx >= en)
                break;

            st = maxx + 1;
            i = j - 1;
        }
        cout<<res;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值