题意:在一片草场上:有一条长度为L (1 <= L <= 1,000,000,L为偶数)的线 段。 John的N (1 <= N <= 1000) 头奶牛都沿着草场上这条线段吃草,每头 牛的活动范围是一个开区间(S,E),S,E都是整数。不同奶牛的活动范围可以 有重叠。
John要在这条线段上安装喷水头灌溉草场。每个喷水头的喷洒半径可以随 意调节,调节范围是 [A,B ](1 <= A <= B <= 1000),A,B都是整数。要求
线段上的每个整点恰好位于一个喷水头的喷洒范围内 每头奶牛的活动范围要位于一个喷水头的喷洒范围内 任何喷水头的喷洒范围不可越过线段的两端(左端是0,右端是L )
John要在这条线段上安装喷水头灌溉草场。每个喷水头的喷洒半径可以随 意调节,调节范围是 [A,B ](1 <= A <= B <= 1000),A,B都是整数。要求
线段上的每个整点恰好位于一个喷水头的喷洒范围内 每头奶牛的活动范围要位于一个喷水头的喷洒范围内 任何喷水头的喷洒范围不可越过线段的两端(左端是0,右端是L )
请问, John 最少需要安装多少个喷水头。
思路(参考郭炜老师课件):从线段的起点向终点安装喷水头,令f(X)表示:所安装喷水头的喷洒范围 恰好覆盖直线上的区间[0 X]时,最少需要多少个喷水头。显然,X应满足下列条件 1、X为偶数
2、X所在位置不会出现奶牛,即X不属于任何一个(S,E)
3、X≥2A
4、当X>2B时,存在Y∈[X-2B X-2A]且Y满足上述三个条件,使得 f(X)=f(Y)+1。由此得到
初始条件: f(X)=1: 2A≤X≤2B 、且X位于任何奶牛的活动范围之外;
状态转移方程:f(X)=1+min{f(Y): Y∈[X-2B X-2A]、Y位于任何奶牛的活动范围 之外}: X>2B。
关键在于快速找到[X-2B X-2A]中使得f(Y)最小的元素,有优先队列维护即可。注意stl优先队列用结构体类型的写法。
再有一点需要注意是如何快速判断一个点处是否有奶牛出没,而已在之前做一遍初始化,方法见代码。
#include <cstdio>
#include <string>
#include <queue>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define INF 0x3fffffff
using namespace std;
#define N 1000005
#define INF 0x3fffffff
struct node{
int x,f;
node(int xx=0,int ff=0):x(xx),f(ff){}
bool operator<(const node &x) const {
return f>x.f;
}
};
priority_queue<node> q;
int c[1005][2],dp[N],flag[N];
int n,m,a,b;
int main(){
int i,k;
memset(flag, 0, sizeof(flag));
scanf("%d %d %d %d",&n,&m,&a,&b);
for(i = 0;i<n;i++){
scanf("%d %d",&c[i][0],&c[i][1]);
flag[c[i][0]+1]++;//为了判断一个点处是否有奶牛出没
flag[c[i][1]]--;
}
for(i = k = 0;i<=m;i++){
k += flag[i];
flag[i] = k>0;//说明有奶牛出没
dp[i] = INF;
}
a <<= 1;
b <<= 1;
for(i = a;i<=b;i+=2)
if(!flag[i]){//如果没有奶牛
dp[i] = 1;
if(b+2-a >= i)//在求F[i]的时候,要确保队列里的点x<= i - a
q.push(node(i,1));
}
for(i = b+2;i<=m;i+=2){
if(!flag[i]){
while(!q.empty()){
struct node tmp = q.top();
if(tmp.x < i-b){//范围之外的点直接剔除
q.pop();
continue;
}
dp[i] = tmp.f+1;
break;
}
}
if(dp[i+2-a] != INF)//队列中增加一个可达下个点的点
q.push(node(i+2-a,dp[i+2-a]));
}
if(dp[m] == INF)
printf("-1\n");
else
printf("%d\n",dp[m]);
return 0;
}