uva10382(贪心算法---区间覆盖问题)

来源:互联网 发布:日本人彻底疯了 知乎 编辑:程序博客网 时间:2024/05/29 16:32

https://vjudge.net/problem/UVA-10382(uva10382 点击打开题目链接)

区间覆盖问题:数轴上有n个闭区间【a-i,b-i】(i是下标),选择尽量少的区间覆盖一条指定线段【s,t】。

思路:转化成草坪矩形上边界(或下边界)的区间覆盖问题。

分析:因为圆的圆心都在草坪中间,所以如果能覆盖矩形的上边界,就一定能覆盖矩形的下边界。每个圆形洒水器的洒水范围(即n个闭区间)可以通过勾股定理求得,因此就转化为了在矩形上边界这条线段(长度为l)上的区间覆盖问题。
(算法竞赛入门经典)该类题的突破口仍然是区间包含和排序扫描,不过要先进行一次预处理。每个区间在【s,t】外的部分都应该先被切掉,因为他们的存在是毫无意义的。预处理后,在相互包含的情况下,小区间显然不应该考虑。(明白是明白了,不过具体到题目就不清楚怎么预处理了)。
把各区间按照a(区间左侧端点)从小到大排序。如果区间1的左端点大于s,无解(因为其他区间的起点更大,不可能覆盖到s点),否则选择起点小于s的最长区间(至于怎么选择最长区间,这道题就卡在这儿了,看了好多题解代码,最后终于理解的差不多了)。选择此区间【a-i,b-i】后,新的起点应该设置为b-i,并且忽略所有区间在b-i之前的部分,就像预处理一样。

代码:

#include <iostream>#include <string>#include <cstring>#include <algorithm>#include <cmath>#include <vector>#include <cstdio>#include <map>#include <cstdlib>#define pi 2*acos(0)using namespace std;const int maxn = 10000 + 10;struct node{    double l,r;    node(double ll,double rr):l(ll),r(rr){}    bool operator < (const node x)const//重载小于运算符,进行排序(该题是按照左侧端点从小到大排序,有时候也按照右侧端点排序)    {        return l < x.l;    }};vector<node> v;int main(){    int n;    double l,w;    while(~scanf("%d%lf%lf",&n,&l,&w))    {        v.clear();//清空很重要        int a,b;        for(int i = 0;i < n;i++)        {            scanf("%d%d",&a,&b);            if(b * 2.0 <= w)//特例判断,如果整个圆都在矩形里面,一定不选                continue;            double p = sqrt((b*1.0)*(b*1.0) - (w/2.0)*(w/2.0));//勾股定理,求出半个区间长度,这里结合图形好好理解,如果理解不了,就做不出题目来。            v.push_back(node(a - p,a + p));//将区间长度存入容器        }        sort(v.begin(),v.end());//切记:排序!!!        int ans = 1;        double sl = 0.0,sr = 0.0;//之前看的代码有这一部分,去掉之后仍然ac,因为下面的循环包含了这种情况//        if(v.empty() || v[0].l > 0.0)//        {//            printf("-1\n");//            continue;//        }        for(int i = 0;i < v.size();i++)        {            if(v[i].l <= sl)//左侧的点比左界限小,但是要选一个最长的区间            {            //如果有多组数据,都满足左侧点比左界限小,那就一直更新右界限,选择最大的区间。                sr = max(sr,v[i].r);            }            else            {            //直到左侧的点比左界限大了,计数加一                ans++;                sl = sr;//新的起点(或者需要维护的点)设置为‘b-i’(即sr).                if(v[i].l > sl)//如果新的区间左侧端点大于起点,不符合情况,跳出循环                    break;                sr = v[i].r;//否则,更新右边界            }            if(sr >= l)                break;        }        if(sr >= l)            printf("%d\n",ans);        else            printf("-1\n");    }    return 0;}
  • 贪心—区间覆盖,还差的很远啊,继续加油吧。
原创粉丝点击