牛客多校3 - Journey(建图,最短路)

https://ac.nowcoder.com/acm/contest/33188/J

题意
给定一个 n 个点的图,每个点都是一个十字路口,和相邻的四个十字路口之间连边。

从一条路径经过十字路口直走,左转或者掉头,花费 1 ;右转不花费。

对于每个十字路口 i i i 给出 c i , 1 , c i , 2 , c i , 3 , c i , 4 c_{i,1}, c_{i,2} , c_{i,3} , c_{i,4} ci,1,ci,2,ci,3,ci,4,逆时针依次为相邻的四个十字路口编号。

现在给出出发路径 < s 1 , s 2 > <s1,s2> <s1,s2>,求到达​终点路径 < t 1 , t 2 > <t1,t2> <t1,t2> 的最小花费。

注意路径方向, < a , b > <a,b> <a,b> < b , a > <b, a> <b,a> 不同。

2 ≤ n ≤ 500000 2≤n≤500000 2n500000

思路
把每一条路径看作一个点,然后两条路径之间连边,新边权根据两条路径的位置赋值,重新建图。
每个十字路口最多产生 8 个新点,所以最终点数不超过 8*n,最多产生 12 条新边,所以最终点数不超过 12*n

对于把两个点哈希成一个点,有两种方法:

  1. (x, y) 哈希成 x*n + y,然后用 unordered_map 映射这个大数。
  2. 直接用 map 映射 pair,但是 map 每次查询的复杂度高,而unordered_map不能直接映射pair。但是可以重写 unordered_map 的比较方式后映射 pair。
struct cmp{
	int operator()(PII A)const{
		return A.fi * 521427 + A.se;
	}
};
unordered_map<PII, int, cmp> mp;

Code

#include<bits/stdc++.h>
using namespace std;

#define Ios ios::sync_with_stdio(false),cin.tie(0)
#define PII pair<int,int>
#define fi first
#define se second
#define endl '\n'

const int N = 4000010, mod = 1e9+7;
int T, n, m;
int a[4];
vector<PII> e[N];
int st, en;
int f[N], dist[N];
int idx;

struct cmp{
	int operator()(PII A)const{
		return A.fi * 521427 + A.se;
	}
};

unordered_map<PII, int, cmp> mp;

int get(int x, int y){
	if(mp.count({x, y})) return mp[{x, y}];
	mp[{x, y}] = ++idx;
	return idx;
}

void dij()
{
	for(int i=1;i<=idx;i++) dist[i] = 1e9;
	dist[st] = 0;
	
	deque<int> que;
	que.push_front(st);
	
	while(que.size())
	{
		int x = que.front(); que.pop_front();
		if(f[x]) continue;
		f[x] = 1;
		
		for(auto it : e[x])
		{
			int tx = it.fi, dis = it.se;
			if(dist[tx] > dist[x] + dis)
			{
				dist[tx] = dist[x] + dis;
				if(dis) que.push_back(tx);
				else que.push_front(tx);
			}
		}
	}
}

signed main(){
	scanf("%lld", &n);
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<4;j++) scanf("%lld", &a[j]);
		for(int j=0;j<4;j++)
		{
			if(a[j] && a[(j+1)%4]){
				int x = get(a[j], i), y = get(i, a[(j+1)%4]);
				e[x].push_back({y, 0});
				x = get(a[(j+1)%4], i), y = get(i, a[j]);
				e[x].push_back({y, 1});
			}
			
			if(a[j])
			{
				int x = get(a[j], i), y = get(i, a[j]);
				e[x].push_back({y, 1});
//				e[y].push_back({x, 1});
			}
		}
		for(int j=0;j<2;j++)
		{
			if(a[j] == 0 || a[(j+2)%4] == 0) continue;
			int x = get(a[j], i), y = get(i, a[(j+2)%4]);
			e[x].push_back({y, 1});
			x = get(a[(j+2)%4], i), y = get(i, a[j]);
			e[x].push_back({y, 1});
		}
	}
	
	int a, b, c, d;
	scanf("%lld%lld%lld%lld", &a, &b, &c, &d);
	st = get(a, b), en = get(c, d);
	
	dij();
	
	if(dist[en] != 1e9) cout << dist[en];
	else cout << -1;
	
	return 0;
}

经验
要敢想,敢建图!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值