NKOI 3662 划区灌溉

划区灌溉

Time Limit:10000MS  Memory Limit:65536K
Total Submit:47 Accepted:25
Case Time Limit:1000MS

Description

约翰的奶牛们发现山脊上的草特别美味.为了维持草的生长,约翰打算安装若干喷灌器.为简化问题,山脊可以看成一维的数轴,长为L(1≤L≤10^6),而且L-定是一个偶数.每个喷灌器可以双向喷灌,并有确定的射程,该射程不短于A,不长于B,A,B(1≤A≤B≤10^3)都是给出的正整数.它所在位置的两边射程内,都属它的灌溉区域.注意,一个喷灌器往左右两边喷射的距离是一样的,比如往左喷的距离是x,那么往右也是x,(A<=x&lt;=B)。
现要求山脊的每一个区域都被灌溉到,喷灌器不能将水喷到山脊以外的区域,而且喷灌器的灌溉区域不允许重叠, 约翰有N(1≤N≤10^3)只奶牛,每一只都有特别喜爱的草区,第i奶牛喜爱的草区是[Si,Ei],不同奶牛的草区可以重叠(Ei-Si<=2*B).现要求,每只奶牛的草区仅被一个喷灌器灌溉. 寻找最少需要的喷灌器数目.

Input

第一行,两个整数N和L
第二行,两个整数A和B
接下来N行,每行两个整数S和E(0 <= S < E <= L),表示每头奶牛喜欢的草区的起止位置

Output

一行,一个整数,表示最少需要的喷灌器的数量,若无解,输出-1

Sample Input

样例输入1:
2 8
1 2
6 7
3 6

样例输入2:
4 202
10 12
21 27
32 39
103 121
163 180

Sample Output

样例输出1:
3

样例输出2:
10

Source

usaco 2004 dec gold


分析1: 
题意告知,一个喷灌器可以往左右两边喷射x远的距离,A<=x<=B
为方便讨论,我们可以将喷射器看做只能往左喷,往左喷射的距离是2*A到2*B之间。
分析2:
我们考虑最简单的情况,没有奶牛:既是覆盖L区域,最少需要多少个喷灌器 
者就是一个简单的背包DP
Dp[i]表示在覆盖区间[1,i],所需最少喷灌器数量。并且在第i位置要安置一个喷灌器。
Dp[i]=Min{ Dp[k] }+1     i-2*B<=k<=i-2*A
0<=i<=L
分析3:
上述方程是O(n^2)的,显然会超时。
我们注意到方程中Min{Dp[k]} 其实是在[i-2*B,i-2*A]区间中找一个最小的Dp[]值
求区间最小值,我们可以考虑"滑动窗口"模型,采用单调队列
这样时间复杂度就能降到O(n)
分析4:
哪些位置是不能放喷灌器呢?
1.根据题意,L是偶数,喷灌器不能喷射到[1,L]区间之外,也就是说只能在偶数位置安置喷灌器
2.每头奶牛所管辖区间只能被一个喷灌器覆盖,
  也就是说对于一头所在区间为[St,Ed]的奶牛,[St+1,Ed-1]位置是不能安放喷灌器的。
  只能安置在St或Ed位置
结论: 
综合上述分析,我们只需预先将不能安放喷灌器的位置i标记为Mark[i]=true,
然后再跑一遍用单调队列优化的DP就可以解决这道题目了。时间复杂度为O(n)
#include<cstdio>
#include<iostream>
#include<queue>
#define inf 999999999
using namespace std;
int n,l,a,b;
deque<int>q;
bool check[1000001];
int f[1000001];
int main(){
	scanf("%d%d%d%d",&n,&l,&a,&b);
	int i,j,k,x,y;
	for(i=1;i<=n;i++){
		scanf("%d%d",&x,&y);
		for(j=x+1;j<y;j++)check[j]=true;//奶牛的区域内设为不能放置
	}
	for(i=1;i<=l;i++){
		f[i]=inf;
		while(!q.empty()&&q.front()<i-2*b)q.pop_front();//从队首删除喷不到该位置的元素
		if(!check[i]&&i%2==0)
		    if(!q.empty())f[i]=min(f[i],f[q.front()]+1);//由于单调性,队首元素的dp值一定最小
        if(i-2*a+1>=0&&!check[i-2*a+1]&&(i-2*a+1)%2==0){//为讨论i+1位置做准备,讨论区间[i-2*B,i-2*A]右边i-2*A+1位置是否入队
        	while(!q.empty()&&f[i-2*a+1]<f[q.back()])q.pop_back();//维护单调性
        	q.push_back(i-2*a+1);
		} 
	}
	printf("%d",f[l]==inf?-1:f[l]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值