洛谷 P1491 集合位置

题意

给定一张 n n n m m m 边的无向图,第 i i i 个点坐标为 ( x i , y i ) (x_i,y_i) (xi,yi),求 1 → n 1 \to n 1n非严格次短路(不允许重复经过点和边)。

思路

我们采用删边的思想,先跑一遍最短路,记录路径,然后依次删掉最短路上每条边,分别跑一遍最短路,再取个最小值即可。

为什么这是正确的呢?首先,次短路至少有一条边不属于最短路。其次,我们不用枚举不在最短路上的边,因为删这些边对最短路没有影响。

至于如何删边,不需要直接在邻接表中找到后再删,这没必要,还会超时,只需要在跑最短路的时候,传入两个参数,分别是要删掉的边的两个端点,只要当前边相连的是这两个点,就直接忽略掉(注意无向图)。

typedef double real;
typedef pair<int, double> PID;
typedef pair<double, int> PDI;
const real INF = 1e20;

struct Graph{
	int n;
	vector<vector<PID>> G;
	vector<real> dis;
	vector<int> pre;
	vector<bool> vis;
	
	Graph(int _n): n(_n){
		G.resize(n);
	}
	
	// Add an undirected edge <u, v> to the graph with edge weight `w`。
	void insert(int u, int v, real w){
		G[u].push_back({v, w});
		G[v].push_back({u, w});
	}
	
	// Starting from s, run the shortest path, ignoring edges (du, dv).
	// If (du, dv) = (-1, -1), then no edge will be deleted and the path will be recorded in `pre`.
	void dij(int s, int du = -1, int dv = -1){
		dis.assign(n, INF);
		vis.assign(n, false);
		if(du == -1 && dv == -1) pre.assign(n, -1);
		
		priority_queue<PDI, vector<PDI>, greater<PDI>> q;
		dis[s] = 0.;
		q.push({0., s});
		
		while(q.size()){
			int u = q.top().second;
			q.pop();
			
			if(vis[u]) continue;
			vis[u] = true;

			for(auto &edge: G[u]){
				int v = edge.first;
				real w = edge.second;
				if((du == u && dv == v) || (du == v && dv == u)) continue;
				if(dis[v] > dis[u] + w){
					dis[v] = dis[u] + w;
					q.push({dis[v], v});
					if(du == -1 && dv == -1) pre[v] = u;
				}
			}
		}
	}
};

代码

#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
#define int long long

typedef double real;
typedef pair<int, double> PID;
typedef pair<double, int> PDI;
const real INF = 1e20;

struct Graph{
	int n;
	vector<vector<PID>> G;
	vector<real> dis;
	vector<int> pre;
	vector<bool> vis;
	
	Graph(int _n): n(_n){
		G.resize(n);
	}
	
	void insert(int u, int v, real w){
		G[u].push_back({v, w});
		G[v].push_back({u, w});
	}
	
	void dij(int s, int du = -1, int dv = -1){
		dis.assign(n, INF);
		vis.assign(n, false);
		if(du == -1 && dv == -1) pre.assign(n, -1);
		
		priority_queue<PDI, vector<PDI>, greater<PDI>> q;
		dis[s] = 0.;
		q.push({0., s});
		
		while(q.size()){
			int u = q.top().second;
			q.pop();
			
			if(vis[u]) continue;
			vis[u] = true;

			for(auto &edge: G[u]){
				int v = edge.first;
				real w = edge.second;
				if((du == u && dv == v) || (du == v && dv == u)) continue;
				if(dis[v] > dis[u] + w){
					dis[v] = dis[u] + w;
					q.push({dis[v], v});
					if(du == -1 && dv == -1) pre[v] = u;
				}
			}
		}
	}
};

real dist(real x1, real y1, real x2, real y2){
	real p = x1 - x2, q = y1 - y2;
	return sqrt(p * p + q * q);
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n, m;
	cin >> n >> m;
	
	vector<real> x(n), y(n);
	Graph G(n);
	for(int i = 0; i < n; i++) cin >> x[i] >> y[i];
	for(int i = 0, u, v; i < m; i++){
		cin >> u >> v;
		u--, v--;
		
		real w = dist(x[u], y[u], x[v], y[v]);
		G.insert(u, v, w);
	}
	
	G.dij(0);
	
	real ans = INF;
	for(int i = n - 1; i; i = G.pre[i]){
		G.dij(0, i, G.pre[i]);
		ans = min(ans, G.dis.back());
	}
	
	if(ans >= INF) printf("-1\n");
	else printf("%.2lf\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值