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
每次都从新设定的起点开始,选择结束时间最晚的
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后,把所有与它相交的排除
一开始就想往区间不相交上去靠,靠出来的结果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点经常会踩到,就是排序后打乱了顺序,输出的时候顺序不对