poj 1769

给一段长为n的线段,m个区间,要求不改变顺序,找出最少个区间使得覆盖1-n。

显然可以得出这个dp公式dp[i][j]表示更新到第i个区间,到第j个位置最小要几个区间。

dp[i][j]=min(dp[i][j],min(s1,e1)+1);。 s1与e1 表示第i个区间,且j在这个区间里。

那么可以转换为这么个dp

dp[i]表示第1个数字可以移到i所需要的最少区间数。

dp[i]=min(dp[i],min(s1,e1)+1)       i在s1与e1之间,这里有个疑问,为什么可以只更新i这个点呢

因为每次更新最远能达到的i,必定这个区间覆盖了了之前最远达到的i与这个i(也就是最远能达到区间被扩大了)。

这时这个能把最远距离扩大的区间必定包含有i能达到最远的地方。(因为这个区间的左端点大于之前的i能到达的最远的话,这么是无法扩大的)

这个区间的左端点有==之前的i能到达的最远和<之前的i能到达的最远两种情况。

如果左端点是等于之前的i,那么就相当与把这个区间扩大了然后区间数量为之前到i的区间数量+1。

如果左端点小于之前的i(这时包含了这个i),又有2种情况,到i的区间数量为最小值,此时对区间变大无影响,

如果不是到i的区间数量为最小值,也就是说可能是到比如i-5这个点的区间数量为最小值,这个区间的扩大

就是根据这个i-5了,但是i-5最小,说明之前有那么一个区间扩大的时候把i-5给覆盖了,也就是说有那么一个区间的终点>=i-5且要求的区间数量和其相同,

那么也必然再这个扩张更大的区间里,所以只改变区间的右端点是可行的。

tle原本和线段树优化的dp

#include <queue>
#include<iostream>
#include<stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <ctime> 
#include <queue>
using namespace std;
int dp[55000];
int main(){
	int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;
//	freopen("in.txt","r",stdin);
	cin >> n>> m;
		for(i=0;i<=n;i++)
	dp[i]=1e9+7;
	dp[1]=0;  //移动到1的费用为0 
	for(i=1;i<=m;i++){
		scanf("%d %d",&t1,&t2);
		for(j=t1;j<=t2;j++){
			if(j==t1){
				f1=dp[j];
				if(f1+1>=dp[t2])break;
			}else if(j>t1&&j<=t2){
				dp[j]=min(dp[j],f1+1);
			}
	//	cout << j <<"的费用为"  << dp[j] << endl; 
		}
	}
	cout << dp[n] << endl;
	return 0;
}
#include <queue>
#include<iostream>
#include<stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <ctime> 
#include <queue>
using namespace std;
int const maxn=5e4+7;
struct ttt{
	int l,r,m;
};
ttt tree[maxn*4];
void build(int l,int r,int num){
	tree[num].l=l;
	tree[num].r=r; 
//	cout << num <<"的" << l <<"    " << r<< endl;
	if(l==r){
		if(l==1)tree[num].m=0; //单点更新的线段树 
		else{
			tree[num].m=1e9+7;
	//	cout << l << "的最值为" <<tree[l].m << endl;
		}
		return ;
	}
	int m=(l+r)/2;
	build(l,m,num+num);
	build(m+1,r,num+num+1);
	tree[num].m=min(tree[num+num].m,tree[num+num+1].m);
//	cout << l <<"    " << r<< "   的最值是"<< tree[num].m  << endl;
}
int update(int x,int y,int num){ 
	if(tree[num].l==tree[num].r){
		tree[num].m=min(tree[num].m,y);
//		cout << tree[num].l  <<"改为" <<y << endl;
		return 0;
	}
	int mid=(tree[num].l+tree[num].r)/2;
	if(mid>=x)update(x,y,num+num);
	else update(x,y,num+num+1);
	tree[num].m=min(tree[num+num].m,tree[num+num+1].m);
}
int gg;
int query(int l,int r,int num){
	if(tree[num].l>=l&&r>=tree[num].r){
		gg=min(gg,tree[num].m);
		return 0;
	}
//	cout << l << "   " << r << "   " << tree[num].l  <<"      " << tree[num].r<< endl;
	int mid=(tree[num].l+tree[num].r)/2;
	
//	cout << l << "   " << r << "   " << tree[num].l  <<"      " << tree[num].r<< "    " <<mid<<endl;
	if(l>mid){
		query(l,r,num+num+1);
	}else if(r<=mid){
		query(l,r,num+num);
	}else if(r>mid&&l<=mid){
		query(l,r,num+num);
		query(l,r,num+num+1);
	}
}


int main(){
	int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;
	freopen("in.txt","r",stdin);
	cin >> n>> m;
	build(1,n,1);
	for(i=1;i<=m;i++){
		scanf("%d %d",&t1,&t2);
		gg=1e9+7;
		query(t1,t2,1); //最小值为gg 
		update(t2,gg+1,1);
	}
	gg=1e9+7;
	query(n,n,1);
	cout << gg << endl;
	return 0;
}




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值