• Stall Reservations


    Stall Reservations

    给出n个区间,第i个区间表示为([l_i,r_i]),询问把这些最少的分组数,使得每组内每个区间相离,(1 <= N <= 50,000)

    区间问题,首要考虑排序,因为涉及分组,于是得保存组的信息,不妨设(a_i)为分的第i组的最后一个区间的右端点

    思路一:按左端点排序

    现在来考虑第i个区间的决策点,对于能分进的组j,必然是(a_j<l_j),第i个区间才能被分进去,但是选哪一个组,按照朴素的思想,必然是选(a_j)最小的或者最大的,因为是按左端点排序,后面的区间左端点必然增大,于是前面区间能分进的组,后面的区间也能分进,于是无论选哪一个组都无所谓,如果不能选,必然要新开一个组。

    现在的问题是如果能分进一个组,会不会开一个新组让结果更加优秀,此时微扰法就不好证明了,于是采取决策包容,注意到新开一个组,它能够选择的范围只有第i个区间所包含的位置,而不新开一个组,以后新开一个组,与它同等优秀,但是组的最后一个区间的右端点可以是任意一个区间的右端点,于是后者决策包含前者决策,选后者一定不会差,得证。

    思路二:按右端点排序

    同样考虑第i个区间的决策点,从已经分的组里面考虑,对于第i个区间能被分进的组j,必然(a_j<l_i),那么对于后面的区间右端点是递增的,而左端点的位置不确定,因此i可以选的,j也可以选,能接下来的决策造成了影响,不满足无后效性原则。

    总上,我们就只要枚举每一个区间,已经每一个可以分进的组,随便选择一个,而不能分进已存在的组,就新开一个组,这样就可以(O(n^2))解决这个问题了。

    但是太慢了,考虑优化,按照特殊化简单化问题的思想,我们只要随便选择一个可以分进的组,不妨选个最特殊的(a_i)最小的组,而且如果它都不能满足条件,所有已经存在的组都不能满足条件,这样问题就变成快速寻找数列({a_i})的最小值,并且支持修改,st表萎的(不支持动态修改),线段树树状数组可以,只要你愿意写,但是优先队列才是最简单的,因此就可(O(nlogn))解决问题。

    顺便给重载(greater(),less())函数的人提个醒,就是(greater())用的是大于号,后者用的是小于号。

    参考代码:

    #include <iostream>
    #include <cstdio>
    #include <queue>
    #include <vector>
    #include <functional>
    #include <algorithm>
    #define il inline
    #define ri register
    #define Size 50050
    #define intmax 0x7fffffff
    using namespace std;
    struct inter{
    	int l,r,id;
    	il bool operator<(const inter &x)const{
    		return l<x.l;
    	}
    	il bool operator>(const inter &x)const{
    		return l>x.l;
    	}
    }I[Size],t;int ans[Size];
    priority_queue<inter,vector<inter>,greater<inter> >gzy;
    il void read(int&);
    int main(){int n;read(n);
    	for(int i(1);i<=n;++i)
    		read(I[i].l),read(I[i].r),I[i].id=i;
    	sort(I+1,I+n+1),gzy.push((inter){-intmax,1});
    	for(int i(1);i<=n;++i){t=gzy.top();
    		if(t.l<I[i].l)ans[I[i].id]=t.r,t.l=I[i].r,gzy.pop(),gzy.push(t);
    		else ans[I[i].id]=gzy.size()+1,gzy.push((inter){I[i].r,gzy.size()+1});
    	}printf("%lu
    ",gzy.size());
    	for(int i(1);i<=n;++i)printf("%d
    ",ans[i]);
    	return 0;
    }
    il void read(int &x){
    	x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
    	while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    
    

    附:

    提供一种傻逼做法,也就是傻逼作者提供的,他菜鸡地乱类比xor游戏这到题目,数列中任何一个数可以随便选择一个数让他的值变为两数异或值,求最后数列的和的最大值,那题把每个数尽可能最大化(这个是可以线性基做的),然后就是最优方案,证明他现在都不知道,于是他认为该题一次一次分组,每次分组的元素最多,最终就可以是最优解,事实证明他萎了,现在提供他的垃圾代码供各位神奇嘲笑,引以为戒。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #define il inline
    #define ri register
    #define Size 50050
    using namespace std;
    struct inter{
    	int l,r;
    	il bool operator<(const inter&x){
    		return r<x.r;
    	}
    }I[Size];
    bool check[Size];
    int ans[Size],tot,zt;
    il void read(int&);
    int main(){
    	int n;read(n);
    	for(int i(1);i<=n;++i)
    		read(I[i].l),read(I[i].r);
    	sort(I+1,I+n+1);
    	while(tot<n){++zt;
    		for(int i(1),j(0);i<=n;++i){if(check[i])continue;
    			if(I[i].l>j)
    				j=I[i].r,check[i]|=true,ans[i]=zt,++tot;
    		}
    	}printf("%d
    ",zt);
    	for(int i(1);i<=n;++i)
    		printf("%d
    ",ans[i]);
    	return 0;
    }
    il void read(int &x){
    	x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
    	while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    	
    
    
  • 相关阅读:
    【Java EE 学习 21 上】【其它类型的监听器】【使用HttpSessionActivationListener监听session的活化和钝化】
    【Java EE 学习 20】【使用过滤器实现登陆验证、权限认证】【观察者模式和监听器(使用监听器实现统计在线IP、登录IP 、踢人功能)】
    【Java EE 学习 19】【使用过滤器实现全站压缩】【使用ThreadLocal模式解决跨DAO事务回滚问题】
    【stut 逆置正整数】
    【JAVA使用XPath、DOM4J解析XML文件,实现对XML文件的CRUD操作】
    【JAVA与DOM4J实现对XML文档的CRUD操作】
    【JAVA解析XML文件实现CRUD操作】
    【JAVA与XML、dtd约束、Schema约束】
    【JAVA正则表达式综合练习】
    【JAVA正则表达式】
  • 原文地址:https://www.cnblogs.com/a1b3c7d9/p/11216967.html
Copyright © 2020-2023  润新知