题目链接 https://cn.vjudge.net/problem/POJ-1769
【题意】
有总长为
n
n
n 的区间
[
1
,
n
]
[1,n]
[1,n],还有
m
m
m 个小区间
[
x
i
,
y
i
]
(
1
<
=
x
i
<
=
y
i
<
=
n
)
[x_i,y_i] \ (1<=x_i<=y_i<=n)
[xi,yi] (1<=xi<=yi<=n) 现在要求从这
m
m
m 个区间中挑出若干个区间,使得挑选出的区间完全覆盖
[
1
,
n
]
[1,n]
[1,n] ,问最少需要挑出几个小区间,保证有解. (
n
<
=
5
×
1
0
4
,
m
<
=
5
×
1
0
5
n<=5×10^4,m<=5×10^5
n<=5×104,m<=5×105)
【思路】
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示从前
i
i
i 个区间挑选若干个来完全覆盖
[
1
,
j
]
[1,j]
[1,j] 所需要的最少区间数,有状态转移
d
p
[
i
+
1
]
[
j
]
=
{
d
p
[
i
]
[
j
]
(
j
!
=
y
i
)
m
i
n
{
d
p
[
i
]
[
j
]
,
d
p
[
i
]
[
j
′
]
+
1
∣
j
′
∈
[
x
i
,
y
i
]
}
(
j
=
y
i
)
dp[i+1][j]= \begin{cases} dp[i][j] \ (j!=y_i) \\ min\{dp[i][j],dp[i][j']+1|j' \in [x_i,y_i]\} \ (j=y_i) \end{cases}
dp[i+1][j]={dp[i][j] (j!=yi)min{dp[i][j],dp[i][j′]+1∣j′∈[xi,yi]} (j=yi) 递推边界
d
p
[
0
]
[
1
]
=
0
,
d
p
[
0
]
[
j
]
=
i
n
f
(
j
>
1
)
dp[0][1]=0,dp[0][j]=inf(j>1)
dp[0][1]=0,dp[0][j]=inf(j>1)
观察可以发现,计算
d
p
[
i
+
1
]
dp[i+1]
dp[i+1] 时完全依赖于
d
p
[
i
]
dp[i]
dp[i] 的结果,所以可以省去第一个状态,把这个
d
p
dp
dp 变成一维数组,这样对于某个区间
[
x
i
,
y
i
]
[x_i,y_i]
[xi,yi]
d
p
[
y
i
]
=
m
i
n
{
d
p
[
y
i
]
,
d
p
[
j
]
+
1
∣
j
∈
[
x
i
,
y
i
]
}
dp[y_i]=min \{ dp[y_i],dp[j]+1|j \in [x_i,y_i] \}
dp[yi]=min{dp[yi],dp[j]+1∣j∈[xi,yi]} 就可以用线段树维护最小值了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define node tree[id]
#define lson tree[id<<1]
#define rson tree[id<<1|1]
using namespace std;
const int inf=2e9;
const int maxn=50005;
const int maxm=500005;
struct Tree{
int left,right;
int minv;
}tree[maxn<<2];
int n,m;
int x[maxm],y[maxm];
int dp[maxn];
void pushup(int id){node.minv=min(lson.minv,rson.minv);}
void build(int id,int le,int ri){
node.left=le;
node.right=ri;
if(le==ri){
node.minv=dp[le];
return;
}
int mid=(le+ri)>>1;
build(id<<1,le,mid);
build(id<<1|1,mid+1,ri);
pushup(id);
}
void update(int id,int pos,int val){
if(node.left==node.right){
node.minv=val;
return;
}
int mid=(node.left+node.right)>>1;
if(pos<=mid) update(id<<1,pos,val);
else update(id<<1|1,pos,val);
pushup(id);
}
int query(int id,int le,int ri){
if(node.left==le && node.right==ri){
return node.minv;
}
int mid=(node.left+node.right)>>1;
if(ri<=mid) return query(id<<1,le,ri);
else if(le>mid) return query(id<<1|1,le,ri);
else{
return min(query(id<<1,le,mid),query(id<<1|1,mid+1,ri));
}
}
int main(){
while(scanf("%d%d",&n,&m)==2){
for(int i=0;i<m;++i) scanf("%d%d",&x[i],&y[i]);
dp[1]=0;
for(int i=2;i<=n;++i) dp[i]=inf;
build(1,1,n);
for(int i=0;i<m;++i){
int val=query(1,x[i],y[i]);
dp[y[i]]=min(dp[y[i]],val+1);
update(1,y[i],dp[y[i]]);
}
printf("%d\n",dp[n]);
}
return 0;
}