Codeforces 487B. Strip(求区间最值+线段树上的dp)

来源:互联网 发布:做网页用什么软件 编辑:程序博客网 时间:2024/05/16 11:24

B. Strip
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Alexandra has a paper strip with n numbers on it. Let's call them ai from left to right.

Now Alexandra wants to split it into some pieces (possibly 1). For each piece of strip, it must satisfy:

  • Each piece should contain at least l numbers.
  • The difference between the maximal and the minimal number on the piece should be at most s.

Please help Alexandra to find the minimal number of pieces meeting the condition above.

Input

The first line contains three space-separated integers n, s, l (1 ≤ n ≤ 105, 0 ≤ s ≤ 109, 1 ≤ l ≤ 105).

The second line contains n integers ai separated by spaces ( - 109 ≤ ai ≤ 109).

Output

Output the minimal number of strip pieces.

If there are no ways to split the strip, output -1.

Sample test(s)
input
7 2 21 3 1 2 4 1 2
output
3
input
7 2 21 100 1 100 1 100 1
output
-1
Note

For the first sample, we can split the strip into 3 pieces: [1, 3, 1], [2, 4], [1, 2].

For the second sample, we can't let 1 and 100 be on the same piece, so no solution exists.


题意:将一个长度为n的数列划分成m个部分,要求每个部分含有的个数>=L,且每个部分最大值-最小值<=S,

求满足上述两个条件情况下m的最小值。即划分区间个数最小

题目地址:http://codeforces.com/contest/487/problem/B


为了形象地说明,举题目第一个栗子如下:


我们得到上列数列,A1-A7,长度为7

A1=1 , A2=3 , A3=1 , A4=2 , A5=4 , A6=1 , A7=2;

根据题目,每一个区间必须满足两个条件:条件一, 区间内元素个数>=L  ;  条件二, 区间最大值-最小值<=S

对于每个位置 i ,我们找到以 i 为左端点,往右拓展的区间,只考虑满足条件二时候,最远可到的位置是哪里。

A1=1,以1为左端点,我们可以得到7个区间

即 [A1,A1] : 1

     [A1,A2] : 1 3

     [A1,A3] : 1 3 1

     [A1,A4] : 1 3 1 2

     [A1,A5] : 1 3 1 2 4

     [A1,A6] : 1 3 1 2 4 1

     [A1,A7] : 1 3 1 2 4 1 2

我们容易得出每个区间最大值-最小值 的值,定义maxsubmin为每个区间最大值-最小值的值

则:

maxsubmin[A1,A1]=0

maxsubmin[A1,A2]=2

maxsubmin[A1,A3]=2

maxsubmin[A1,A4]=2

maxsubmin[A1,A5]=3

maxsubmin[A1,A6]=3

maxsubmin[A1,A7]=3

栗子一里S=2,意思是任意一个选取的区间maxsubmin值必须<=2

由此可知,从A1开始的区间中只有[A1,A1],[A1,A2],[A1,A3],[A1,A4] 满足条件二

所以,从A1开始,往右拓展的区间,只考虑满足条件二的时候,最远可到达位置为 4


不妨定义 maxto[i] 表示从 i 开始,往右拓展的区间,只考虑条件二的时候,最远可到达的位置为 maxto[i]


对于原数列,我们很容易得出每个位置 i 的值 maxto[i],如下图所示



我们得到了maxto[i],表示的是从位置 i 开始,向右拓展,只考虑满足条件二时,最远可到达的位置为 maxto[i]

容易发现,假设此时要从位置 i 开始往右划分一段区间,根据条件一,区间元素个数必须>=L,也就是,从位置 i 开始,至少需要划到 i + L -1的位置

而当从位置 i 开始,即使划到位置n,区间元素个数也小于L的话,就不能从 i 开始往右划分。因为此时不满足条件一


我们很容易想到特判的条件,当n<L的时候,即数列个数小于L,无法满足条件一

同样地,从位置 i 开始,我已经知道只考虑条件二时最远可达位置 maxto[i],此时如果maxto[i]-i+1,也就是区间元素个数,小于L的话,就不能从 i 往右进行划分;



在以上的基础上,我们就可以动态规划地解决题目的问题

定义dp数组 dp[i]

dp[i] 表示 从1到 i-1这段区间上最小划分的区间个数

同样,以栗子一举例


我们已经知道,从A1开始的区间中只有[A1,A1],[A1,A2],[A1,A3],[A1,A4] 满足条件二

但只有[A1,A2],[A1,A3],[A1,A4] 既满足条件二,又满足条件一

则,dp[3]=1,dp[4]=1,dp[5]=1

拿dp[3]举例,表示从1到2这段去区间上,划分的区间个数为1个,即[A1,A2]这一个区间


我们继续将这个步骤进行下去

如果我们从3处开始往右划分区间,根据之前得出的maxto[3]和题目给的L可以知道,只有[A3,A3],[A3.A4]满足题意

所以dp[4]=min(dp[4],dp[3]+1),dp[5]=min(dp[5],dp[3]+1);

dp[3]+1=2,表示从1到2可以划分最小区间个数为1,从3到3 或 3到4,又可以进行一次划分,所以,从1到3 或 从1到4 可以进行两次划分

但是,我们从1到3 和 1到4 可以进行一次划分,所以根据题意,我们取更小的那次

dp[4]=min(dp[4],dp[3]+1)=1,dp[5]=min(dp[5],dp[3]+1)=1;

这个样子,动态规划的思想就很显然了

对于每个位置 i ,我们从之前的位置维护得到,从1到 i-1 这段区间最少可以划分成几个区间

那么,此时,我们再从 i 位置开始往右划分,再维护 i 位置右边的dp值。这样子answer=dp[n+1],表示1到n区间最少可以划分成几个区间,就是我们要的答案


对于栗子一,我们可以得到一下dp数组:


其中INT表示正无穷,即2之前的区间[1,1]不能划分成区间(违背了条件一)

可以发现,answer=dp[8]=3,就是栗子一的答案


核心转移方程:dp[j]=min(dp[j],dp[i]+1);

初始化:dp[1]=0,dp[其他]=无穷大(实际上,用1e9表示就够了)

而dp伪代码如下:

for(int i=1;i<=n;i++){    dp[i]=无穷大;}dp[1]=0;

for(int i=1;i<=n;i++){    if(从i开始到n这段区间元素个数<L) continue;    if(dp[i]==无穷大) continue;    int to=i+L-1;    if(maxto[i]<to) continue;    for(int j=to+1;j<=maxto[i]+1;j++){        dp[j]=min(dp[j],dp[i]+1);    }}

但是!!

这么做是过不了这道题的=。=

用动态规划做没有问题,dp思想也是正确的,而问题出在复杂度上

可以发现上面伪代码的复杂度是:O(n^2),n=10^5,很显然不可能1s解决问题


所以,这里就涉及到了标题所提到的,在线段树上进行dp

我们会发现上串为代码有这样一个for循环:

for(int j=to+1;j<=maxto[i]+1;j++){        dp[j]=min(dp[j],dp[i]+1);    }

这个for循环,实际上就是,将[to+1,maxto[i]+1]这段区间上dp值dp[j]与dp[i]+1取最小值。这是可以通过线段树懒操作来优化的

具体如何通过线段树来优化,我不愿意详述,相信熟悉线段树(需要用到懒操作)的朋友可以很容易写出来

如果不熟悉线段树的朋友,可以看我下面代码。不过建议多写线段树的懒操作,这样子才能有本质的提高。

线段树dp核心代码如下:

//线段树dp:#define MAXN 100100#define INT (0x3f3f3f3f)*2struct Segment_Tree{    int left,right;    int dp,lazy;}tree[4*MAXN];void plant_tree(int id,int l,int r){    tree[id].left=l,tree[id].right=r;    tree[id].lazy=INT;    if(l==r){        if(l==1) tree[id].dp=0;        else tree[id].dp=INT;        return;    }    int mid=(l+r)>>1;    plant_tree(id<<1,l,mid);    plant_tree((id<<1)+1,mid+1,r);    tree[id].dp=min(tree[id<<1].dp,tree[(id<<1)+1].dp);}void push_down(int id){//懒操作pushdown    if(tree[id].lazy==INT) return;    tree[id].dp=min(tree[id].dp,tree[id].lazy);    if(tree[id].left==tree[id].right){        tree[id].lazy=INT;        return;    }    tree[id<<1].lazy=min(tree[id<<1].lazy,tree[id].lazy);    tree[(id<<1)+1].lazy=min(tree[(id<<1)+1].lazy,tree[id].lazy);    tree[id].lazy=INT;}void update_leaf(int id,int l,int r,int val){    if(tree[id].left==l && tree[id].right==r){        tree[id].lazy=min(tree[id].lazy,val);        push_down(id);        return;    }    push_down(id);    int mid=(tree[id].left+tree[id].right)>>1;    if(r<=mid) update_leaf(id<<1,l,r,val);    else if(mid<l) update_leaf((id<<1)+1,l,r,val);    else{        update_leaf(id<<1,l,mid,val);        update_leaf((id<<1)+1,mid+1,r,val);    }}int query(int id,int l,int r){    if(tree[id].left==l && tree[id].right==r){        push_down(id);        return tree[id].dp;    }    push_down(id);    int mid=(tree[id].left+tree[id].right)>>1;    if(r<=mid) return query(id<<1,l,r);    else if(mid<l) return query((id<<1)+1,l,r);    else return min(query(id<<1,l,mid),query((id<<1)+1,mid+1,r));}


此题需要用到两棵线段树,一棵来查询a[i]任意区间上的最大最小值,另外一棵线段树就是用来动态规划解决问题

总代码如下:

//Hello. I'm Peter.#include<cstdio>#include<iostream>#include<sstream>#include<cstring>#include<string>#include<cmath>#include<cstdlib>#include<algorithm>#include<functional>#include<cctype>#include<ctime>#include<stack>#include<queue>#include<vector>#include<set>#include<map>using namespace std;typedef long long ll;typedef long double ld;#define peter cout<<"i am peter"<<endl#define input freopen("data.txt","r",stdin)#define randin srand((unsigned int)time(NULL))#define INT (0x3f3f3f3f)*2#define LL (0x3f3f3f3f3f3f3f3f)*2#define gsize(a) (int)a.size()#define len(a) (int)strlen(a)#define slen(s) (int)s.length()#define pb(a) push_back(a)#define clr(a) memset(a,0,sizeof(a))#define clr_minus1(a) memset(a,-1,sizeof(a))#define clr_INT(a) memset(a,INT,sizeof(a))#define clr_true(a) memset(a,true,sizeof(a))#define clr_false(a) memset(a,false,sizeof(a))#define clr_queue(q) while(!q.empty()) q.pop()#define clr_stack(s) while(!s.empty()) s.pop()#define rep(i, a, b) for (int i = a; i < b; i++)#define dep(i, a, b) for (int i = a; i > b; i--)#define repin(i, a, b) for (int i = a; i <= b; i++)#define depin(i, a, b) for (int i = a; i >= b; i--)#define pi 3.1415926535898#define eps 1e-6#define MOD 1000000007#define MAXN 100100#define N#define M//线段树求最值struct Segment_TreeforRMQ{    int left,right;    int min,max;}treeRMQ[4*MAXN];void plant_treeforRMQ(int id,int l,int r,Segment_TreeforRMQ* tree,int *a){    tree[id].left=l,tree[id].right=r;    if(l==r){        tree[id].min=tree[id].max=a[l];        return;    }    int mid=(l+r)>>1;    plant_treeforRMQ(id<<1,l,mid,tree,a);    plant_treeforRMQ((id<<1)+1,mid+1,r,tree,a);    tree[id].min=min(tree[id<<1].min,tree[(id<<1)+1].min);    tree[id].max=max(tree[id<<1].max,tree[(id<<1)+1].max);}int query_min(int id,int l,int r,Segment_TreeforRMQ* tree){    if(tree[id].left==l && tree[id].right==r){        return tree[id].min;    }    int mid=(tree[id].left+tree[id].right)>>1;    if(r<=mid) return query_min(id<<1,l,r,tree);    else if(mid<l) return query_min((id<<1)+1,l,r,tree);    else return min(query_min(id<<1,l,mid,tree),query_min((id<<1)+1,mid+1,r,tree));}int query_max(int id,int l,int r,Segment_TreeforRMQ* tree){    if(tree[id].left==l && tree[id].right==r){        return tree[id].max;    }    int mid=(tree[id].left+tree[id].right)>>1;    if(r<=mid) return query_max(id<<1,l,r,tree);    else if(mid<l) return query_max((id<<1)+1,l,r,tree);    else return max(query_max(id<<1,l,mid,tree),query_max((id<<1)+1,mid+1,r,tree));}//查询最大值-最小值int maxsubmin(int from,int to){    return query_max(1,from,to,treeRMQ)-query_min(1,from,to,treeRMQ);}//线段树dp:struct Segment_Tree{    int left,right;    int dp,lazy;}tree[4*MAXN];void plant_tree(int id,int l,int r){    tree[id].left=l,tree[id].right=r;    tree[id].lazy=INT;    if(l==r){        if(l==1) tree[id].dp=0;        else tree[id].dp=INT;        return;    }    int mid=(l+r)>>1;    plant_tree(id<<1,l,mid);    plant_tree((id<<1)+1,mid+1,r);    tree[id].dp=min(tree[id<<1].dp,tree[(id<<1)+1].dp);}void push_down(int id){//懒操作pushdown    if(tree[id].lazy==INT) return;    tree[id].dp=min(tree[id].dp,tree[id].lazy);    if(tree[id].left==tree[id].right){        tree[id].lazy=INT;        return;    }    tree[id<<1].lazy=min(tree[id<<1].lazy,tree[id].lazy);    tree[(id<<1)+1].lazy=min(tree[(id<<1)+1].lazy,tree[id].lazy);    tree[id].lazy=INT;}void update_leaf(int id,int l,int r,int val){    if(tree[id].left==l && tree[id].right==r){        tree[id].lazy=min(tree[id].lazy,val);        push_down(id);        return;    }    push_down(id);    int mid=(tree[id].left+tree[id].right)>>1;    if(r<=mid) update_leaf(id<<1,l,r,val);    else if(mid<l) update_leaf((id<<1)+1,l,r,val);    else{        update_leaf(id<<1,l,mid,val);        update_leaf((id<<1)+1,mid+1,r,val);    }}int query(int id,int l,int r){    if(tree[id].left==l && tree[id].right==r){        push_down(id);        return tree[id].dp;    }    push_down(id);    int mid=(tree[id].left+tree[id].right)>>1;    if(r<=mid) return query(id<<1,l,r);    else if(mid<l) return query((id<<1)+1,l,r);    else return min(query(id<<1,l,mid),query((id<<1)+1,mid+1,r));}void printno(){    printf("%d\n",-1);    exit(0);}int n,s,l;int a[MAXN],maxto[MAXN];int main(){    cin>>n>>s>>l;    repin(i,1,n){        scanf("%d",a+i);    }    if(n<l) printno();//当n<l的时候,不成立    plant_treeforRMQ(1,1,n,treeRMQ,a);//建立树,为了求a数组的最值    int to=1;    repin(i,1,n){//求出只满足maxi-mini<=s的条件下,每个点i最远可往右到哪个点        to=max(to,i);        while(1){            if(to==n){                maxto[i]=to;                break;            }            int t=maxsubmin(i,to+1);            if(t<=s) to+=1;            else{                maxto[i]=to;                break;            }        }    }    plant_tree(1,1,n+1);//线段树dp,建树    repin(i,1,n){//dp,用线段树优化        if(n-i+1<l) continue;        int now=query(1,i,i);        if(now==INT) continue;        int to=i+l-1;        if(maxto[i]<to) continue;        update_leaf(1,to+1,maxto[i]+1,now+1);    }    int ans=query(1,n+1,n+1);    if(ans==INT) ans=-1;    printf("%d\n",ans);}


0 0
原创粉丝点击