POJ 1769线段树维护dp

我们设dp[i][j]为做到第i个sort时候,最大值被移动到j需要转移用的数目。
dp[i][j]=dp[i-1][j](if(ti!=j) 即移动不到当前最大值
dp[i][j]=min(dp[i-1][j], dp[i-1][k] (si<=k<=ti)+1(if(ti=j))即这个最大值包含在这个区间。
我们发现dp值只与他前一个有关,故可以开滚动数组,而且可以知道,以前的状态是不用清除的,所以直接开一维数组来滚动即可。

dp[ti]=min(dp[ti],dp[k] (si<=k<=ti)+1)
这里我们可以发现了,这么做其实还是n*m的时间复杂度,不过这种形式我们可以用线段树来优化,单点更新,dp[ti] ,区间询问si,ki的最小值,正是线段树最有用的地方。

#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<cstdio>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const long long INF = 0x3f3f3f3f;
using namespace std;
typedef pair<int, int> pir;
int n, m;
int tree[1 << 18];//log50000*4
int dp[50005];
int ik[500005];
int jk[500005];
void pushup(int rt)
{
	tree[rt] = min(tree[rt << 1], tree[rt << 1 | 1]);
}//向上推
void init(int rt,int l,int r)
{
	if (l == r)
	{
		tree[rt] = INF;//一开始全是最大值,因为要维护最小值
		return;
	}
	int mid = (l + r) >> 1;
	 init(rt << 1, l, mid);
	 init(rt << 1 | 1, mid + 1, r);
	 pushup(rt);
}//初始化
void update(int jk, int k, int l, int r, int rt)
{
	int mid = (l + r) >> 1;
	if (l == jk && r == jk)//达到叶子节点更新
	{
		tree[rt] = min(tree[rt], k);
		return;
	}
	if (jk <= mid)update(jk, k, l, mid, rt<<1);
	else update(jk, k, mid + 1,r, rt << 1 | 1);
	pushup(rt);//向上推,维护区间的最小值
}//更新
int querry(int l, int r, int rt, int a, int b)
{
	int ans = INF;
	if (b < l)return INF;
	if (a > r)return INF;
	if (l == a && r == b)//区间包裹,返回区间值
	{
		return tree[rt];
	}//因为可能多个区间都放问了一遍,所以需要一起求最小值
	int mid = (l + r) >> 1;
	if (b <= mid)ans = min(ans, querry(l, mid, rt << 1, a, b));
	else if (a > mid) ans = min(ans, querry(mid + 1, r, rt << 1 | 1, a, b));
	else ans = min(ans, min(querry(l, mid, rt << 1, a, mid), querry(mid + 1, r, rt << 1 | 1, mid + 1, b)));
	//注意这种情况要变化a,b的值,因为从中间断开了
	return ans;
}
int main()
{
	cin >> n >> m;
	up(i, 0, m)
	{
		scanf("%d %d", &ik[i], &jk[i]);
	}
	upd(i, 1, n)
	{
		dp[i] = INF;
	}
	dp[1] = 0;//初始化
	init(1, 1, n);
	update(1, 0, 1, n, 1);
	up(i, 0, m)
	{ 
		dp[jk[i]] = min(dp[jk[i]], querry(1, n, 1, ik[i], jk[i])+1);
		update(jk[i], dp[jk[i]], 1, n, 1);
	}
	cout << dp[n];//最后输出最大值n可以达到的最小值
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值