(使数组每个区间无重复且整个数组字典序最小)-HDU-6301

题目链接

问题:

有一组n个正整数。对于每两个元素a

在区间 a[L]~a[R] 中,所有的元素的不相等
找到一个符合事实的 字典序最小的列。

输入:

第一行输入包含一个整数T,表示测试用例的数量。

第二行两个整数n和m(1≤n,m≤1e5) n 是数组 的长度,m是区间数。

接下来的m行中

的每一行包含两个整数  L   和   R。

输出:

Sample Input

3

2 1

1 2


4 2

1 2

3 4


5 2

1 3

2 4

Sample Output

1 2

1 2 1 2

1 2 3 1 1


分析:

1.将所有的区间 优先按 L 升序,其次按 R 升序。(划重点

     原因:这样有序的排列后,,不会因为有重合的区间而使得已经确定的值又改变。

                 根据字典序最小原则,先确定的数字之后就不会再改变

2. 如果当前处理的区间包含于上一个处理的区间,那么当前这个不用处理,continue。。

                                       上个     ---------------------

                                        当前      ---------------

3.存在位置没有被区间覆盖。那么字典序最小原则,这个位置是  1.。。。。比如

                                      上个    ------------------

                                       当前                             (1) ----

4. 如果当前 区间 L 在上个区间里, R 在上个区间外。那么从上个区间的后一位开始处理。也就是*****的位置

                                     上个   --------------------

                                      当前     -----------------!*******


一、对于第三种情况,可以 for(1~n)每次处理以 数字  i  为 L 的所有区间,,如果没有,可以直接赋值为  1。。

因为之后的区间的  L  肯定会更大,所以这个位置就是 1 ,这也是这种排序方式的好处

 

二、最困难的是第四种情况。!。如何确定 *******  位置的数字,使得区间里的数不重复,且数列字典序最小。

         错误思路:

                               最先想到的是,直接把  ******  像 1,2....这样赋值,显然是错误的。

                                如果     -------------

                                             -------------********   像这样两个区间 L  重合,那么 * 就不能是第一个区间里的任何数!!!    


              究竟哪些数字可以用,哪些数字不能用,并且怎样用才能使数列的字典序最小????

              AC代码的巧妙之处就是用了优先队列来记录可以使用的数字,并且是升序,所以能保证字典序最小。

              首先把优先队列初始化为  (1~n),每用一个删除一个,

              还要去维护,添加前面虽然在某一位使用过

             但是之后不会处理到那一位上的数字


             下面来讨论 究竟什么数字可以使用并且怎么去实现。。

               一切的处理方法都是建立在对所有区间的排序方法上。。

             for(i  in  1~n)     大循环意思是 按 1~n 的顺序 , 每次处理  L  为  i  的所有区间,

             {

                       while (k < m && a[k].L == i)     //  对  L  为  i  的所有区间做处理、、、
                      { k++;}

                        // 注意  !!!程序走到这里时,L = i 的所有区间已经全部处理完了,!!!

                       //    下一个 处理的是  L = i+1 的 所有区间,

                       //      也就是说当前的  第   i  位  之后永远不会处理到~~!!

                     //     所以  就可以把  填在第   i   位的数字放入优先队列中  供之后使用

              }

 

#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

const int MAXN = 1000005;

struct node
{
	int l,r;
}a[MAXN];

// 记录最终答案的数组
int ans[MAXN];

// 按区间的左端点从小到大,左端点相等按右端点从小到大
bool cmp(node a,node b)
{
	if (a.l == b.l)
        return a.r < b.r;
	return a.l < b.l;
}
// flag 标记最终ans中出现的元素
int flag[MAXN];
// 从小到大的优先队列
priority_queue<int,vector<int>,greater<int> > que;

int main()
{
	int t;
	while (scanf("%d",&t)!=EOF)
	{
        while (t--)
        {
            // n个元素,m个区间
            int n,m;
            scanf("%d%d",&n,&m);
            // 把m个区间存入结构体中
            for (int i = 0;i < m; ++i)
            {
                scanf("%d%d",&a[i].l,&a[i].r);
            }
            // 对m个区间排序,按左端点从小到大,左端点相同按右端点从小到大
            sort(a,a+m,cmp);
            // 初始化 数列为1 2 3 .... n
            // ans 初始化为 0
            for (int i = 1;i <= n; ++i)
            {
                que.push(i);
                ans[i] = 0;
                flag[i] = 0;
            }
            // pos记录上一次处理结束的下一个位置的坐标,
            // 也就是上一个区间的右端点的后一位
            // 同时pos 是每次需要处理的位置的坐标
            int pos = 1,k = 0;

            for (int i = 1;i <= n; ++i)
            {
                // 处理以 i 为左端点的每个区间
                // k表示第k个区间
                while (k < m && a[k].l == i)
                {
                    // 比较pos与当前需要处理的区间的左端点的大小
                    // 取大的数作为这一次处理的起点
                    // 大的数一定在上一个区间的右边
                    pos = max(i,pos);
                    // 正式开始处理这个区间
                    while (pos <= a[k].r)
                    {
                        ans[pos] = que.top();
                        flag[ans[pos]] = 1;
                        que.pop();
                        pos++;
                    }
                    // 下一个区间
                    k++;
                }
                //保证区间内的数不同 ans数组再往前走 用过的位置的数已经可以使用
                // 到这里是以当前 i 为 左端点的区间都处理完了

                // ans[i] = 0表示并没有处理到第i个数,
                // 也就是前面的所有区间都没有覆盖到 ans[i]
                // 字典序最小原则,前面的区间没有被处理到,就赋值为1
                if (ans[i] == 0)
                    ans[i] = 1;
                //
                else if (flag[ans[i]] == 1)
                {
                    que.push(ans[i]);
                    flag[ans[i]] = 0;
                }
            }

            for (int i = 1;i <= n; ++i)
            {
                printf("%d%c",ans[i],i == n ? '\n' : ' ');
            }
            // 把数列清空
            while (!que.empty())
                que.pop();

    }
	}

	return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值