给一段长为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;
}