poj2376区间贪心

  1. 题意
    题意:给出n段区间和区间长度T(从1开始),以下n行为每段区间的首位地址,求最少需要几段将所有区间覆盖,如果不能全覆盖就输出-1.
  2. 思路
    这道题要先注意能不能完全覆盖,如果n段区间打头的没有1,那就直接输出-1.如果计算结尾不是T也是输出-1.
    还有一个要考虑的,容易曲解题意的一点,选取的区间只要完全覆盖就行,但不要求每一段都重叠,假如总长度为7,可以由1~3和4~7来组成。
    然后考虑解决方法,想用尽可能少的区间,如何去选取,假设给一个例子,长度为20,有7段,分别是
    (1,5),(2,6),(3,10),(4,9),(6,15),(10,18),(17,20)
    先从区间能否完全覆盖考虑,从1开始,有20结束,符合。
    再从最左端考虑,头是1,尾是5,在1~5区间内包含(2,6),(3,10),(4,9)这三个区间的一部分,另外还有(6,15)这个区间可以和1~5区间进行连接,那我们这一步可以考虑的区间就有这4个了。那选哪一个最合适?x显然是6~15,因为这个区间最右边是15,最靠近终点。这就是贪心的方向。接下来重复上一次步骤,看看(6~)15以内都有哪些区间:只有(10,18)接壤,所以这一次选到18。下一次选17~20,结束。
    由于我给的例子是排好序的,但是给的数据是无序的,所以需要自己排好序,按照起始位置从小到大,如果起始位置一样就按照结束位置排。
  3. 代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
struct A
{
    int tou;   //起始位置
    int wei;   //结束位置
};
A a[30000];
int T,n;
int cmp(A a,A b)
{
    if(a.tou != b.tou)
        return a.tou < b.tou;  //按起始位置升序
    else
        return a.wei > b.wei;  //如果起始位置相同则按结束位置降序
}
void input()
{
    for(int i = 0 ; i < n ; i++)
        cin>>a[i].tou>>a[i].wei;
}

void solve()
{
    sort(a,a+n,cmp);
    int i = 0,r = 0,t = 0,ans = 1;  //因为默认已选第一段所以ans初始化为1
    r = a[i].wei; //最右边为第一段结尾
    t = r;        //t记录当前最右位置
    if(a[i++].tou != 1) cout<<-1<<endl; //这是排除未能覆盖
    else
    {
        for( ; i < n && r != T ; ans++ )
        {
            bool no_higher = true;  //能否再往后找的标记
            while( i < n && a[i].tou <= r + 1 ) //寻找与当前区间可以连接在一块能组成的最长区域
            {

                if( a[i].wei > t )  //记录当前能到达的最右位置
                    t = a[i].wei,no_higher = false;
                i++;
                if(t == T) break;
            }
            if( no_higher ) //如果找不到更高的,也就是出现"断层"就中断
                break;
            else
                r = t; //将当前右侧达到的位置更新
        }
        if(r == T) cout<<ans<<endl; //如果全覆盖,你懂的
        else       cout<<-1<<endl;
    }
}
int main()
{
    #ifdef H_R
        freopen("in.txt","r",stdin);
    #endif // H_R
    ios::sync_with_stdio(false);
    cin.tie(false);
    while(cin>>n>>T)
    {
        input();
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值