贪心 区间相关(贪心策略+注意细节) 一定一定要学会造样例啊

1、区间选择最少的点覆盖

poj .3069 Saruman's Army  //大白例题

写那种找满足条件的数的时候还是用while循环更加优雅一些啊,我写的for跟狗屎一样,主要还是一开始贪心的姿势错了

这个博主画的图很直观,赞一个

思路:和反转一排碗的问题一样,要先考虑最左边的

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int r,n;
const int N=1e3+10;
int loc[N];
int vis[N];
int main()
{
    while(cin>>r>>n)
    {

        if(r==-1&&n==-1) break;
        for(int i=1;i<=n;++i)
            cin>>loc[i];
        sort(loc+1,loc+1+n);
        int ans=0,i=1;
        while(i<=n)
        {
            int left=loc[i++];//对于最左边的点,把最靠右的能覆盖它的点给标记了
            while(i<=n&&left+r>=loc[i]) ++i;
            left=loc[i-1];//
            while(i<=n&&left+r>=loc[i]) ++i;//重新找到一个未被标记的最靠左的点
            ++ans; //每标记一个点答案++
        }
        cout<<ans<<endl;

    }
        /*int last=1;
        int ans=0;

        for(int i=1;i<=n;++i)
        {
           if(loc[i]-loc[last]>r)  //从last开始计算距离 已经超了 ,<r就继续向左走
            {
                //i-1是<=r范围内的最后一个
                if(i-1==last&&i==2){++ans;last=2;vis[1]=1; continue;}
                if(i-1==last&&loc[last]-loc[last-1]<=r)//向右没找到并且向左也没有能覆盖的
                {
                    last=i;
                    continue;
                }
                else
                {
                    vis[i-1]=1;
                    last=i;
                    ++ans;
                }

            }
        }
        ++ans;   //把包含最后一个位置的组给加进去
        cout<<ans<<endl;
    }*/   //还是思维错了

    return 0;
}

2、区间选点问题  (取尽量少的点,使得每个区间[a,b]内都至少有一个点,不同区间包含的点可以是同一个)LRJ紫书P233

小区间满足时大区间一定满足,所以在区间包含的情况下,大区间不需要考虑

把所有区间按b从小到大排序(b相同时a从大到小排序),若出现区间包含情况,小区间一定在前面

贪心策略:取区间最后一个点

poj 1328 Radar Installation 如果雷达点只能是整数的话,也不能退化成上面一种情况,一开始贪心策略错了,大白上这个区间的模板真好用啊。

把能覆盖到第i个岛的雷达位置计算出来,则问题转换成了区间选点问题

#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdio.h>
using namespace std;
int n;
double d;
typedef struct dot
{
    int x,y;
};
struct interval
{
    double s,t;
};
bool cmp(interval a,interval b)
{
    if(a.t==b.t)
        return a.s>b.s;
    else return a.t<b.t;
}
dot pnt[1010];
interval inter[1010];
int main()
{
    int kase=0;
    while(cin>>n>>d)
    {
        if(!n&&!d) break;
        int maxx=0;
        for(int i=1;i<=n;++i)
        {
            cin>>pnt[i].x>>pnt[i].y;
            maxx=max(pnt[i].y,maxx);
            inter[i].s=pnt[i].x-sqrt(d*d-pnt[i].y*pnt[i].y);
            inter[i].t=pnt[i].x+sqrt(d*d-pnt[i].y*pnt[i].y);
        }
        ++kase;
        if(maxx>d) {printf("Case %d: -1\n",kase); continue;}
        sort(inter+1,inter+1+n,cmp);
        int ans=0;
        int i=1;
        while(i<=n)//按照LRJ紫皮上的贪心策略写
        {
            double last=inter[i++].t;
            while(i<=n&&inter[i].s<=last) ++i;  //==也是
            ++ans;
            last=inter[i].t;
        }
        printf("Case %d: %d\n",kase,ans);

    }
    return 0;
}

3、区间覆盖问题(一定一定要学会造样例,最好写前把伪代码写好,把每一步可能出现的情况想好

数轴上有n个闭区间[a,b],选择尽量少的区间覆盖一条指定线段[s,t]

step1:预处理:每个区间在[s,t]外的部分切掉;相互包含的情况下,小区间不应该考虑

step2:把各区间按照a从小到大排序,如果区间1的起点不是s,无解,否则选择起点在s的最长区间。选择此区间[ai,bi]后,新的起点设置为bi

step3:回到step1

每次都从新设定的起点开始,选择结束时间最晚的 

poj2376 cleaning shifts

wa点有两个,其中之一是每次从上一个结束的下一个开始就好了,这个属于读题是否细心的问题

wa点之二由hzoi_battleship提供, 6 5

1 4

1 5

4 5

1 1

2 2

3 4 ,我一开始的代码会输出-1,因为第一个被选的是1,5,之后while循环会把i加到7,把我的maxx给修改为0了

这个是思维上的错误,主要是i<=n这个限制条件没拦住,且代码影响了我全局的参数,还是思维的严谨性叭

好奇怪啊,数据被hack了还能过,这样都可能可以( • ̀ω•́ )✧Mark一下https://blog.csdn.net/wmn_wmn/article/details/7909483

// 区间覆盖问题 选择尽量少的区间覆盖1-T
//wa点之一每次从上一个结束的下一个开始就好了
#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=25000+10;
struct cow
{
    int s,t;
};
cow c[N];
int n,T;
bool cmp(cow a,cow b)
{
    if(a.s==b.s)
        return a.t>b.t;   //这样被包含的区间就会跳过了
    else return a.s<b.s;
}

int main()
{
    scanf("%d%d",&n,&T);
    for(int i=1;i<=n;++i)
        scanf("%d%d",&c[i].s,&c[i].t);
    sort(c+1,c+1+n,cmp);
    if(c[1].s>1) {printf("-1\n");return 0;}
   // if(c[n].t<T) {printf("-1\n");return 0;}
    int i=1,ans=1;
    int flag=1; //控制中间能接上
    int last=c[1].t;
    int maxx;  //能够记录最后一次覆盖到哪
    while(i<=n)
    {
        maxx=c[i].t;
        //if(last>=T) break;  //6 5 1 4 1 5 4 5 1 1 2 2 3 4 这里不break的话,i会循环到7,导致maxx变为0,-1退出
        //也就是说我在后面的判断中没有控制好i的范围,导致关键参数被修改
        while(i<=n&&c[i].t<=last) ++i;  //被包含的直接跳过
//        cout<<i<<endl;
        if(c[i].s-last>1) {flag=0;break;} //没有接上  原先写的>0算是读题错误叭。。
        else if(i<=n)//是这里没控制好范围  看前面的题也会发现,每次while里面讨论的时候都会有i<=n
        {

            maxx=c[i].t;
            while(i<=n&&c[i].s-last<=1)//在符合时间的情况下选择结束最晚的牛
            {
                maxx=max(maxx,c[i].t);
               // cout<<i<<":"<<maxx<<endl;
                ++i;
            }
            ++ans;
            last=maxx;
        }
    }

    if(maxx<T) printf("-1\n");
    else if(!flag) printf("-1\n");
    else printf("%d\n",ans);
    return 0;
}

4、区间不相交

数轴上有n个开区间(ai,bi),选择尽量多个区间,使得它们两两没有公共点

假设区间x完全包含区间y,那么选区间x是不划算的,b按从小到大排序,一定选第一个区间

选了区间1后,把所有与它相交的排除

poj 3190 stall reservations

一开始就想往区间不相交上去靠,靠出来的结果x,n-x是需要的stall数,但是这个过程中不好保存哪个牛对应哪个stall

本题复习了优先权队列,一直觉得优先权队列自定义优先级很amazing,sort里 return <是按照升序排列,

bool operator < 中return >是越小的越在前面

#include<iostream>
#include<queue>
#include<algorithm>
#include<stdio.h>
using namespace std;
const int maxn=5e4+10;
struct cow  //怎么由结束时间t反向追踪到栏号呢,emmm ,傻了啊优先权队列里面直接存cow
{
    int s,t;
    int id; //按照开始时间排序后要按照原来的顺序输出,忘记这一点了
    int stall;
    friend bool operator <(cow a,cow b)
    {
        return a.t>b.t;       //结束时间早的优先级高
    }
   //通过自定义operator<操作符来比较元素中的优先级。
    //在重载”<”时,最好不要重载”>”,可能会发生编译错误

};
cow c[maxn];
int n;
priority_queue<cow> pq;
bool cmp1(cow a,cow b)
{
    if(a.s==b.s) return a.t<b.t;
    else return a.s<b.s;
}
bool cmp2(cow a,cow b)
{
    return a.id<b.id;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%d%d",&c[i].s,&c[i].t);
        c[i].id=i;
    }
    sort(c+1,c+1+n,cmp1);
    int cnt=0;
    ++cnt;
    c[1].stall=cnt;  //差点弄错顺序,先把stall存进去才行
    pq.push(c[1]);

    for(int i=2;i<=n;++i)
    {
        if(!pq.empty())
        {
            cow tmp=pq.top();
            if(tmp.t>=c[i].s)                    //与区间1存在相交
             {
                 ++cnt;
                 c[i].stall=cnt;
                 pq.push(c[i]);

             }
             else                                //与区间1不想交可共享
             {
                 c[i].stall=tmp.stall;
                 pq.pop();                       //把区间1扔出来
                 pq.push(c[i]);
             }
        }

    }
    printf("%d\n",cnt);
    sort(c+1,c+1+n,cmp2);
    for(int i=1;i<=n;++i)
        printf("%d\n",c[i].stall);

    return 0;
}

还有一个wa点经常会踩到,就是排序后打乱了顺序,输出的时候顺序不对

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值