HDU 6301 - Distinct Values [2018杭电多校联赛第一场 D](贪心)

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6301

【题意】
你需要构造一个长度为n的整数序列,且要符合要求:有m个区间,使得序列中每个区间[L,R]内部的元素不能重复,构造符合要求且字典序最小的序列并输出

【输入格式】
多组输入,第一行为数据组数
每组数据第一行是n和m(m,n<=1e5)
接下来有m行,每行有两个整数代表一个区间

【输出格式】
每组数据输出一行,即构造出的序列,用空格隔开

【思路】
        自己当时做感觉也是贪心,但怎么也搞不出来,T的停不下来,还是太菜了,看过题解才懂的。首先这些区间要稍作处理,我们用一个数组R来存以当前位置为左端点的区间对应的右端点是多少,也就是说R[i]表示序列中[ i,R[i] ]不能有重复元素。当然要初始化R[i]=i,表示区间只有它自己,根据输入不断更新。
        然后就是求解的过程了,从左到右依次处理每一个位置。因为要获得字典序最小的序列,所以每次尽量放最小的值进去,所以我用了一个优先队列que来存放现在可以使用的所有序号,每次给序列赋值的时候用的都是堆顶元素。然后我又用了两个变量ok和now,ok表示的是做上一次处理的左端点,ok表示当前处理到的最右端点。这两个变量都是从小到大递增的,不会有回溯过程,当前处理位置i时,如果发现ok>=R[i],说明这里对应的区间早已处理过,不需要在处理了。如果ok< R[i],说明从now+1到R[i]还没处理,而处理这一段之前需要把[ok,i-1]对应的序号放入优先队列que中,因为处在上次的区间但不在这次区间里的序号又可以用了。

#include<bits/stdc++.h>
using namespace std;

const int maxn=100050;

int n,m;
int a[maxn];//a是最终的序列
int R[maxn];//R[i]表示区间[i,R[i]]中不能有重复元素
priority_queue<int,vector<int>,greater<int> > que;

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        while(que.size()) que.pop();
        for(int i=1;i<=n;++i) {
            que.push(i);
            R[i]=i;
        }
        for(int i=0;i<m;++i){
            int x,y;
            scanf("%d%d",&x,&y);
            R[x]=max(R[x],y);
        }

        int ok=1,now=0; //ok是上次处理完的区间起始位置,now是当前处理到的最右端位置
        for(int i=1;i<=n;++i){  //从左到右依次处理每个点对应的区间
            if(now>=R[i]) continue; //[i,R[i]]的所有元素都被处理过了,直接看下一个位置
            while(ok<i){    //把上次区间起始位置到当前区间起始位置[ok,i-1]的这一段数字重新加入队列
                que.push(a[ok]);
                ++ok;
            }//同时处理完之后ok=i,恰好是这次处理的起点
            while(now<R[i]){//处理当前的这一段,尽量赋最小值
                a[++now]=que.top();
                que.pop();
            }
        }

        for(int i=1;i<=n;++i) printf("%d%c",a[i],i==n?'\n':' ');
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiao_k666/article/details/81176581