1. 程式人生 > 其它 >題解 P5683 [CSP-J2019 江西] 道路拆除

題解 P5683 [CSP-J2019 江西] 道路拆除

題解 P5683 [CSP-J2019 江西] 道路拆除

Link

給定一個 \(n\) 個點 \(m\) 條邊的無向圖,求兩條路徑 \(1\to s_1,1 \to s_2\) ,使得兩條路徑的長度分別不大於 \(t_1,t_2\) ,且兩條路徑用到的所有邊的總數量最少。你需要輸出 \(m\) 減用到的邊數

\(1 \leq n,m \leq 3\times 10^3\)

考場上亂搞拿了 35pts

首先,如果直接求 \(1\) 到這兩個點的最短路,然後求這兩條最短路用到的邊的總數是不對的,因為如果公共部分多一點,可能會更優。

經過一段時間的觀察,我們容易發現:公共部分一定是一條連續的鏈,因為如果鏈的中間斷開了,那麼把更長的那一條變成更短的那一條,路徑長度變少了,公共部分變長了,肯定更優。

於是就可以列舉這個分界點 \(k\) ,這兩條路徑就被拆成了三條互不相關的路徑 \(1\to k ,k \to s_1 ,k \to s_2\) ,答案最小意味著這三條路徑都是最短路。

於是從 \(1,s_1,s_2\) \(bfs\) 三次,就可以求出這三條最短路的長度了( \(k \to s_1\) 在無向圖中的最短路徑長度等於 \(s_1\to k\) 的最短路徑長度。)

注意列舉 \(1\to k \to s_1\) 的長度 \(\leq t_1\)\(1 \to k \to s_2\) 的長度 \(\leq t_2\)

程式碼如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
inline int read() {
	int num = 0 ,f = 1; char c = getchar();
	while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
	while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
	return num * f;
}
const int N = 3e3 + 5 ,M = 6e3 + 5 ,INF = 0x3f3f3f3f;
struct Edge {
	int to ,next;
	Edge (int to = 0 ,int next = 0) : to(to) ,next(next) {}
}G[M]; int head[N] ,idx;
inline void add(int u ,int v) {
	G[++idx] = Edge(v ,head[u]); head[u] = idx;
	G[++idx] = Edge(u ,head[v]); head[v] = idx;
}
int n ,m ,t1 ,t2 ,A ,B;
inline void bfs(int s ,int dis[]) {
	for (int i = 1; i <= n; i++) dis[i] = INF;
	queue <int> q;
	q.push(s); dis[s] = 0;
	while (!q.empty()) {
		int now = q.front(); q.pop();
		for (int i = head[now]; i ; i = G[i].next) {
			int v = G[i].to;
			if (dis[v] == INF) {
				dis[v] = dis[now] + 1;
				q.push(v);
			}
		}
	}
}
int dis1[N] ,dis2[N] ,dis3[N];
signed main() {
	n = read() ,m = read();
	for (int i = 1; i <= m; i++) {
		int u = read() ,v = read();
		add(u ,v);
	}
	A = read() ,t1 = read() ,B = read() ,t2 = read();
	bfs(1 ,dis1);
	bfs(A ,dis2);
	bfs(B ,dis3);
	int ans = INF;
	for (int i = 1; i <= n; i++)
		if (dis1[i] + dis2[i] <= t1 && dis1[i] + dis3[i] <= t2)
			ans = min(ans ,dis1[i] + dis2[i] + dis3[i]);
    //這裡就算三條路徑有重複也沒關係,因為如果有重複意味著在別的分界點有更優的答案
	if (ans == INF) puts("-1");
	else printf("%d\n" ,m - ans);
	return 0;
}