POJ-2373-Dividing the Path【单调队列优化dp】

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[i2B]~ d p [ i − 2 A ] ) + 1 dp[i-2A])+1 dp[i2A])+1
为了保证一些区间只能用一个洒水装置浇水,那么这些区间就不能出现洒水器浇灌区间的端点,即dp[i]不能从这个区间段中转移出来(可以标记一下这些点)

维护区间[i-2B,i-2A]的最小值(单调队列优化):随着i的增大,区间[i-2B,i-2A]逐渐往后移,所以下标值靠前且数值较大的dp值其实是不需要考虑的(根据这一点维护队列的队尾,队首根据区间的左端点值维护 ),比如区间{3456 ,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]);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值