题意:
给定一个n个点(n<=3000)所有边长为1的图,求最多可以删掉多少条边后,图满足s1到t1的距离小于l1,s2到t2的距离小于l2。
Solution:
首先可以分两种情况讨论:
1:假设最后留下的边是互不相交的两条路径。此时的答案是n-s1到t1的最短路径-s2到t2的最短路径。
2:假设最后留下的边有重合的一段,此时只要枚举重合的这一段的起点和终点,就可以判断。注意此时要考虑这两条路径经过枚举的两点的顺序。
限制的条件比较多,可以用来剪枝的条件也很多。
由于所有边的长度为1,用DFS或者SPFA算法可以很快地求出最短路。
#include <bits/stdc++.h> using namespace std; const int N = 3009; struct edge { int v, ne; } E[N * N << 1]; int head[N], cnt; int n, m, ans; int s1, s2, t1, t2, l1, l2; int dis[5][N]; int vis[N]; void SPFA (int S, int k) { queue<int> q; memset (vis, 0, sizeof vis); dis[k][S] = 0; vis[S] = 1; q.push (S); while (!q.empty() ) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = E[i].ne) { int v = E[i].v; if (dis[k][u] + 1 < dis[k][v]) { dis[k][v] = dis[k][u] + 1; if (!vis[v]) { vis[v] = 1; q.push (v); } } } vis[u] = 0; } } inline void add (int u, int v) { E[++cnt].v = v, E[cnt].ne = head[u]; head[u] = cnt; } int main() { ios::sync_with_stdio (0); cin >> n >> m; for (int i = 1, u, v; i <= m; ++i) { cin >> u >> v; add (u, v), add (v, u); } cin >> s1 >> t1 >> l1; cin >> s2 >> t2 >> l2; memset (dis, 0x1f, sizeof dis); SPFA (s1, 0); SPFA (s2, 1); SPFA (t1, 2); SPFA (t2, 3); if (dis[0][t1] <= l1 && dis[1][t2] <= l2) { ans = dis[0][t1] + dis[1][t2]; for (int i = 1; i <= n; ++i) { int flag = 0; for (int k = 1; k <= n; ++k) dis[4][k] = 33333; for (int j = i + 1; j <= n; ++j) { int tem1 = min (dis[0][i] + dis[2][j], dis[0][j] + dis[2][i]); int tem2 = min (dis[1][i] + dis[3][j], dis[1][j] + dis[3][i]); if (tem1 + tem2 < ans && tem1 < l1 && tem2 < l2) { if (!flag) { SPFA (i, 4); flag = 1; } if (tem1 + dis[4][j] <= l1 && tem2 + dis[4][j] <= l2) { ans = min (ans, tem1 + tem2 + dis[4][j]); } } } } } else { ans = m + 1; } cout << m - ans << endl; }