您的位置:首页 > Web前端

BZOJ3387: [Usaco2004 Dec]Fence Obstacle Course栅栏行动

2016-08-22 08:30 337 查看
题目大意:给定一个初始点和n个与x轴平行的y轴坐标互不相同的栅栏,问水平距离至少移动多少能使得从该初始点回到原点且不从中间跨越任何一条栅栏

首先可以确定,最终行进的路线一定可以等价于在几个栅栏的边界和起点终点间连线产生的路径,也就是说只有栅栏的边界是有用的,所以我们可以在这些点上建边跑最短路
但是这样的话边数是N^2级别的,所以我们要优化一下建图
考虑什么样的边是有效的,对于一个栅栏端点来说,只有向上第一个拦住他的栅栏是有效的,因为其他的栅栏都可以通过垂直向上走来绕过去而这个不能
所以我们就可以只将每个点与向上第一个拦住他的栅栏的两个端点连边,这样可以将边数降成O(N)级别的了
具体实现可以用set

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#define N 1000010
using namespace std;
set<int>S;
map<int,int>P;
int to
,nxt
,pre
,w
,cnt;
void ae(int ff,int tt,int ww)
{
cnt++;
to[cnt]=tt;
nxt[cnt]=pre[ff];
w[cnt]=ww;
pre[ff]=cnt;
}
int q[10*N],h,t;
int d
;
bool zai
;
void spfa()
{
int i,j,x,y;
memset(d,0x3f3f3f3f,sizeof(d));
d[1]=0;q[1]=1;
h=t=1;
while(h<=t)
{
x=q[h];h++;zai[x]=false;
for(i=pre[x];i;i=nxt[i])
{
j=to[i];
if(d[j]>d[x]+w[i])
{
d[j]=d[x]+w[i];
if(!zai[j])
{
t++;
q[t]=j;
zai[j]=true;
}
}
}
}
}
int main()
{
int k,s;
scanf("%d%d",&k,&s);
int i,j;
int cn=1;
P[0]=1;S.insert(0);
int x,y;
set<int>::iterator it;
for(i=1;i<=k;i++)
{
scanf("%d%d",&x,&y);
it=S.lower_bound(x);
while(it!=S.end()&&(*it)<=y)
{
j=(*it);
ae(P[j],cn+1,j-x);
ae(P[j],cn+2,y-j);
it++;
S.erase(j);
}
cn++;P[x]=cn;
cn++;P[y]=cn;
S.insert(x);S.insert(y);
}
cn++;
for(it=S.begin();it!=S.end();it++)
ae(P[(*it)],cn,abs(s-(*it)));
spfa();
printf("%d",d[cn]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: