https://vjudge.net/problem/POJ-2373
题意:农夫约翰要用洒水装置给长为L的山脊浇水,并且保证每个洒水装置的浇灌范围不会重叠,也不会流经山脊的末端(每个洒水装置的浇灌半径为A~B的整数)。约翰还有n头奶牛,每头奶牛都有一段特别喜欢的区间,这些区间只能用一个洒水装置浇水。问洒水装置最少需要用多少个。
思路:
转移方程:
d
p
[
i
]
=
m
i
n
(
d
p
[
i
−
2
B
]
dp[i]=min(dp[i-2B]
dp[i]=min(dp[i−2B]~
d
p
[
i
−
2
A
]
)
+
1
dp[i-2A])+1
dp[i−2A])+1
为了保证一些区间只能用一个洒水装置浇水,那么这些区间就不能出现洒水器浇灌区间的端点,即dp[i]不能从这个区间段中转移出来(可以标记一下这些点)
维护区间[i-2B,i-2A]的最小值(单调队列优化):随着i的增大,区间[i-2B,i-2A]逐渐往后移,所以下标值靠前且数值较大的dp值其实是不需要考虑的(根据这一点维护队列的队尾,队首根据区间的左端点值维护 ),比如区间{3 ,4 ,5 ,6 ,2,7 ,4,6},保留下来的值形成的是一个单增的数列,最小值就是队首元素,我们可以用一个数组来存这些值的下标。
单调队列的一般应用:
1、维护区间最值
2、优化DP
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
using namespace std;
#define LL long long
#define uLL unsigned long long
#define PII pair<int,int>
const int manx=1e6+10;
const int INF=0x3f3f;
int dp[manx],head,tail,q[manx],vs[manx];
int main()
{
int N,L,A,B;
PII cover[1010];
memset(vs,0,sizeof vs);
memset(dp,-1,sizeof dp);
scanf("%d%d%d%d",&N,&L,&A,&B);
for(int i=0;i<N;i++)
scanf("%d%d",&cover[i].first,&cover[i].second);
sort(cover,cover+N);
int p=-1;
for(int i=0;i<=N;i++){
int l=max(p+1,cover[i].first+1),r=cover[i].second-1;
for(p=l;p<=r;p++)
vs[p]=1;
p--;
}
dp[0]=0;
tail=0,head=1;
for(int i=2*A;i<=L;i+=2){
while(head<=tail&&q[head]<i-2*B)head++;
if(dp[i-2*A]!=-1){
while(head<=tail&&dp[q[tail]]>dp[i-2*A])tail--;
q[++tail]=i-2*A;
}
if(!vs[i]&&head<=tail)
dp[i]=dp[q[head]]+1;
}
printf("%d\n",dp[L]);
}