Songwriter CF1252-E(贪心)

题意:

已知一个序列a,问能否将a映射到序列b,使得相邻元素之间的大小关系不变(三种),且相邻元素不能相差超过k,且每个元素范围在[L,R]内。如果能,求字典序最小的b。

思路:

可以设b[1]的范围为[L,R],从前往后遍历求出b[2]~b[n]每个元素的上界和下界。如果其中有元素已经没有可以使之前成立的范围了,那么说明无法构造这样的映射。

此时最后一个元素的上下界是正确的,从后往前遍历即可得到正确序列B,但是若要保证字典序最小,则每个元素应该取最小的可行取值(如果开始先从后往前扫求出范围,再从前往后贪心地选最小取值更容易理解,但是反过来也是正确的贪心策略)。

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int l[maxn],r[maxn],a[maxn];
int ans[maxn];
int main(){
    int n,L,R,k;
    cin>>n>>L>>R>>k;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    l[1]=L;r[1]=R;
    for(int i=2;i<=n;i++){
        if(a[i]==a[i-1]){
            l[i]=l[i-1];
            r[i]=r[i-1];
        }
        else if(a[i]>a[i-1]){
            l[i]=l[i-1]+1;
            r[i]=min(r[i-1]+k,R);
        }
        else if(a[i]<a[i-1]){
            l[i]=max(L,l[i-1]-k);
            r[i]=r[i-1]-1;
        }
        if(l[i]>r[i]||r[i]<L||l[i]>R){
            printf("-1\n");
            return 0;
        }
    }
    ans[n]=l[n];
    for(int i=n-1;i>=1;i--){
        if(a[i]==a[i+1])
            ans[i]=ans[i+1];
        else if(a[i]<a[i+1]){
            //ans[i+1]-k<=ans[i]<=ans[i+1]-1
            //l[i]<=ans[i]<=r[i]
            ans[i]=max(ans[i+1]-k,l[i]);
        }
        else if(a[i]>a[i+1]){
            //ans[i+1]+1<=ans[i]<=ans[i+1]+k
            //l[i]<=ans[i]<=r[i]
            ans[i]=max(ans[i+1]+1,l[i]);
        }
    }
    for(int i=1;i<=n;i++){
        printf("%d ",ans[i]);
    }
    printf("\n");
}

猜你喜欢

转载自www.cnblogs.com/ucprer/p/11801432.html