题目描述 + 讲解
经典例题了,常规解法是 用线段树优化dp数组,dp很好想,线段树优化难写。
这里我给出另一种解法,最短路spfa。(也不好想)
因为只要求x轴上的运动路径。所以只有栅栏的边界是有用的。想象他的总体运动轨迹时,可以以边界为点,相互连线(斜的不影响),之间距离为x轴距离差。
最短路解法的关键是尽量建少的边。
从上面运动到下面,和从下面移动下面是一样的。
因为他输入的数据是从下到上。所以从下至上考虑也可以。
对于之前的端点(不管左右),如果在现在的[x,y]内,可能存在阻拦,然后用左右端点与之建边,并去除改节点。然后储存进现在这两个端点。
set不重复,且自动排序。首先他不影响建边,遇到相同时,建的边长度为0。因为他的不重复,可以少建边。
部分代码:
int n,s,head[maxn],cnt,d[maxn],c;
bool vis[maxn];
set<int>sz;
map<int,int>mp;
struct edge{ int to,w,next; }e[maxn];
void add(int u,int v,int w){
e[++cnt].w=w; e[cnt].to=v;
e[cnt].next=head[u]; head[u]=cnt;
}
void spfa(){
queue<int>q; q.push(1);
memset(d,inf,sizeof(d)); d[1]=0; vis[1]=1;
while(!q.empty()){
int x=q.front(); vis[x]=0; q.pop();
for(int i=head[x];i;i=e[i].next){
int y=e[i].to,w=e[i].w;
if(d[y]>d[x]+w){
d[y]=d[x]+w;
if(!vis[y]){ q.push(y); vis[y]=1; }
}
}
}
}
int main(){
n=read(); s=read();
sz.insert(0); mp[0]=1; c++;
set<int>::iterator it;
for(int i=0;i<n;i++){
int x,y; x=read(); y=read();
it=sz.lower_bound(x);
while(it!=sz.end()){
int t=(*it);
if(t>y) break;
add(mp[t],c+1,t-x);
add(mp[t],c+2,y-t);
it++;
sz.erase(t);
}
mp[x]=++c; mp[y]=++c;
sz.insert(x); sz.insert(y);
}
c++;
for(it=sz.begin();it!=sz.end();it++) add(mp[(*it)],c,abs(s-(*it)));
spfa();
printf("%d",d[c]);
}