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

13 篇文章 0 订阅
10 篇文章 0 订阅
题目大意:给定一个初始点和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[N],nxt[N],pre[N],w[N],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[N];
bool zai[N];
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]);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值