POJ 1769 - Minimizing maximizer(线段树优化DP)

题目链接 https://cn.vjudge.net/problem/POJ-1769

【题意】
有总长为 n n n 的区间 [ 1 , n ] [1,n] [1,n],还有 m m m 个小区间 [ x i , y i ]   ( 1 &lt; = x i &lt; = y i &lt; = n ) [x_i,y_i] \ (1&lt;=x_i&lt;=y_i&lt;=n) [xi,yi] (1<=xi<=yi<=n) 现在要求从这 m m m 个区间中挑出若干个区间,使得挑选出的区间完全覆盖 [ 1 , n ] [1,n] [1,n] ,问最少需要挑出几个小区间,保证有解. ( n &lt; = 5 × 1 0 4 , m &lt; = 5 × 1 0 5 n&lt;=5×10^4,m&lt;=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&#x27;]+1|j&#x27; \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]+1j[xi,yi]} (j=yi) 递推边界 d p [ 0 ] [ 1 ] = 0 , d p [ 0 ] [ j ] = i n f ( j &gt; 1 ) dp[0][1]=0,dp[0][j]=inf(j&gt;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]+1j[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值