题意:
给定n个线段
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]
要求选出的线段满足任意两个线段要么不相交,要么全包含。
问最多能选出多少条线段。
思路:
由部分解推出问题的解,考虑区间DP
如果对于每个线段都考虑:dp[i][j] = max{dp[i][k]+dp[k+1][j]},
显然复杂度为:
O
(
n
3
)
O(n^3)
O(n3),TLE是必然的
线段端点值域很大,需要先离散化一下端点。
状态表示:dp[i][j] 表示区间 [i,j] 的满足条件的最大线段数,即区间 [i,j] 的最优解
状态转移:
-
如
果
没
有
以
i
为
左
端
点
的
线
段
,
那
么
d
p
[
i
]
[
j
]
的
状
态
可
直
接
由
m
a
x
(
d
p
[
i
+
1
]
[
j
]
,
d
p
[
i
]
[
j
−
1
]
)
或
转
移
过
来
如果没有以i为左端点的线段,那么dp[i][j]的状态可直接由max(dp[i+1][j],dp[i][j-1])或转移过来
如果没有以i为左端点的线段,那么dp[i][j]的状态可直接由max(dp[i+1][j],dp[i][j−1])或转移过来
d p [ i ] [ j ] = m a x ( d p [ i + 1 ] [ j ] , d p [ i ] [ j − 1 ] ) ; dp[i][j]=max(dp[i+1][j],dp[i][j-1]); dp[i][j]=max(dp[i+1][j],dp[i][j−1]); -
如
果
有
以
i
为
左
端
点
的
线
段
,
遍
历
这
些
线
段
,
设
线
段
为
[
i
,
k
]
,
k
∈
[
i
,
j
)
则
有
:
如果有以i为左端点的线段,遍历这些线段,设线段为[i,k],k∈[i,j)则有:
如果有以i为左端点的线段,遍历这些线段,设线段为[i,k],k∈[i,j)则有:
d p [ i ] [ j ] = m a x { d p [ i ] [ k ] + d p [ k + 1 ] [ j ] } + ( 是 否 存 在 线 段 [ i , j ] ) ; dp[i][j]=max\{dp[i][k]+dp[k+1][j]\}+(是否存在线段[i,j]); dp[i][j]=max{dp[i][k]+dp[k+1][j]}+(是否存在线段[i,j]);
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e3+10;
int t,n,b[maxn*2],dp[maxn*2][maxn*2],num;
vector<int> g[maxn*2];
struct Seg {
int l,r;
} a[maxn];
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
num=0;
for(int i=0; i<n; i++) {
scanf("%d%d",&a[i].l,&a[i].r);
b[num++]=a[i].l;
b[num++]=a[i].r;
}
//离散化
sort(b,b+num);
num=unique(b,b+num)-b;
for(int i=0; i<num; i++) g[i].clear();
for(int i=0; i<n; i++) {
a[i].l=lower_bound(b,b+num,a[i].l)-b;
a[i].r=lower_bound(b,b+num,a[i].r)-b;
g[a[i].l].push_back(a[i].r);
}
for(int i=0;i<=num;i++){ //重置dp
for(int j=i;j<=num;j++)
dp[i][j]=0;
}
//printf("num=%d\n",num);
for(int len=1; len<=num; len++) { //枚举区间长度len,
//printf("======= len=%d\n",len);
for(int i=0; i<num; i++) { //枚举左端点i,推出右端点j=i+len-1
int j=i+len-1,f=0;
if(j>=num) break;
dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
for(int k:g[i]) { //枚举[i,k]进行状态转移
if(k==j) f=1;
if(k>=j) continue;
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]);
}
dp[i][j]+=f;
//printf("i=%d j=%d dpij=%d\n",i,j,dp[i][j]);
}
}
cout<<dp[0][num-1]<<endl;
}
}