luogu P3831 [SHOI2012]回家的路

题面传送门
对于这道题,我们很容易想到分层图(大概是因为那道分层图优化成矩乘太出名了),则这道题只需要建两层就够了,一层表示横的,一层表示竖的。
首先思考两层图之间怎么连接,很明显,由本层的点向下一层的同一节点建一条双向的边权为 1 1 1的边
其次思考同一层图中怎么建边,我们可以用两个 v e c t o r vector vector来保存每一行和列所有的点的编号,这个点向所有的同一行和同一列的点建边,则建图完成。
最后,将起点与终点分别开一个点,向同一行和列的建边,然后就是跑 S P F A SPFA SPFA,只要正常松弛就可以了
代码实现:

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#define abs(x) ((x)<0?-(x):(x))
#define min(a,b) ((a)>(b)?(b):(a))
using namespace std;
long long n,m,sx,sy,tx,ty,s,t,x[100039],y[100039],d[200039],head,h[200039],now,cur,ans;
struct yyy{
	long long to,w,z;
}tmp,f[2000039];
inline void add(long long x,long long y,long long z){
	f[++head]=(yyy){y,z,h[x]};
	h[x]=head;
}
vector<long long> k1[20039],k2[20039];
queue<long long> q;
int main(){
	memset(h,-1,sizeof(h));
	memset(d,0x3f,sizeof(d));
	register int i,j;
	scanf("%lld%lld",&n,&m);
	for(i=1;i<=m;i++){
		scanf("%lld%lld",&x[i],&y[i]);
		for(j=0;j<k1[x[i]].size();j++) add(i,k1[x[i]][j],abs(y[i]-y[k1[x[i]][j]])*2),add(k1[x[i]][j],i,abs(y[i]-y[k1[x[i]][j]])*2);
		for(j=0;j<k2[y[i]].size();j++) add(i+m,k2[y[i]][j]+m,abs(x[i]-x[k2[y[i]][j]])*2),add(k2[y[i]][j]+m,i+m,abs(x[i]-x[k2[y[i]][j]])*2);
		k1[x[i]].push_back(i);
		k2[y[i]].push_back(i);
	}
	for(i=1;i<=m;i++) add(i,i+m,1),add(i+m,i,1);
	scanf("%lld%lld%lld%lld",&sx,&sy,&tx,&ty);
	for(i=0;i<k1[sx].size();i++) add(0,k1[sx][i],abs(sy-y[k1[sx][i]])*2);
	for(i=0;i<k2[sy].size();i++) add(0,k2[sy][i]+m,abs(sx-x[k2[sy][i]])*2);
	for(i=0;i<k1[tx].size();i++) add(k1[tx][i],2*m+1,abs(ty-y[k1[tx][i]])*2);
	for(i=0;i<k2[ty].size();i++) add(k2[ty][i]+m,2*m+1,abs(tx-x[k2[ty][i]])*2);
	d[0]=0;
	q.push(0);
	while(!q.empty()){
		now=q.front();
		//printf("%lld %lld\n",now,d[now]);
		q.pop();
		cur=h[now];
		while(cur!=-1){
			tmp=f[cur];
			if(d[tmp.to]>d[now]+tmp.w) d[tmp.to]=d[now]+tmp.w,q.push(tmp.to);
			cur=tmp.z;
		}
	}
	if(d[2*m+1]>100000000000) printf("-1");
	else printf("%lld\n",d[2*m+1]);
}

不开 l o n g l o n g long long longlong见祖宗!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值