题意:给出N个矩形的左下角的点
(x1,y1)
、右上角的点
(x2,y2)
。定义矩形
A≤B
,当
A.x2<B.x1
且
A.y2<B.y1
。
求出最长的矩形序列
A1,A2,...,An
,满足
A1≤A2≤A3...≤An
.
思路:可以看到,定义的
A≤B
关系不满足自反性,所以是个DAG上的动态规划。
但是可以计算得到,朴素的动态规划是
Θ(n2)
,明显会超时。
这样看来,必须要优化。
注意到满足题意的矩形序列的点的值是对于x,对于y,都是递增的,可以想到对单调上升子序列的单调队列的优化。
我们首先固定一维x,即将所有点按照x从小到大进行排序。
然后考虑另外的一维y。
和单调上升子序列的单调队列一样,我们定义dp[i]为长度为i的矩形序列的右上角的点yd的最小值。
这样,对于矩形的左下角的点,我们需要找到在dp[i]中更新的位置。
对于矩阵的右上角的点,我们要更新dp[i]中的值。
这样,查找和更新的分离就能完成这道题目了。
最后的结果就是dp数组中,有效数字的数目,同样也可以二分找到。
注意:二元小于运算符的判断写跪了。。。学了一个神奇的姿势。
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 100010;
const int INF = 0x3f3f3f3f;
struct point{
int x, y, id,flag;
point(){}
point(int a, int b, int c,int d): x(a), y(b), id(c),flag(d){}
bool operator <(const point & rhs) const{
return (x < rhs.x) || (!(rhs.x < x) && (flag < rhs.flag));
}
} p[MAX<<1];
int pos[MAX<<1], dp[MAX<<1];
int main(void)
{
//freopen("input.txt","r",stdin);
int T,N;
scanf("%d",&T);
while(T--){
scanf("%d",&N);
for(int i = 0; i < N; ++i){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
p[i<<1] = point(a,b,i,0);
p[i<<1|1] = point(c,d,i,1);
}
N *= 2;
sort(p,p + N);
memset(dp,0x3f,sizeof(dp));
for(int i = 0; i < N; ++i){
if(p[i].flag == 0)
pos[p[i].id] = lower_bound(dp,dp + N, p[i].y) - dp;
else
dp[pos[p[i].id]] = min(dp[pos[p[i].id]],p[i].y);
}
printf("%d\n",lower_bound(dp,dp+N,INF) - dp);
}
return 0;
}