update
This commit is contained in:
parent
1efcad447b
commit
8f81302029
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
/*.txt
|
||||
!/CMakeLists.txt
|
||||
/.*
|
||||
/test*
|
||||
# ---> C++
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
90
src/11/25/U186781.cpp
Normal file
90
src/11/25/U186781.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int MAXN = 40005;
|
||||
const int MAXM = 100005;
|
||||
const long long INF = 1e18;
|
||||
struct Edge {
|
||||
int to;
|
||||
int weight;
|
||||
int next;
|
||||
} edges[MAXM * 2];
|
||||
int head_arr[MAXN];
|
||||
int edge_cnt = 0;
|
||||
void add_edge(int u, int v, int w){
|
||||
edges[edge_cnt].to = v;
|
||||
edges[edge_cnt].weight = w;
|
||||
edges[edge_cnt].next = head_arr[u];
|
||||
head_arr[u] = edge_cnt++;
|
||||
}
|
||||
long long dijkstra(int start, int node1, int blocked_edge1, int blocked_edge2, int n){
|
||||
static long long dist_arr[MAXN];
|
||||
static int visited_version[MAXN];
|
||||
static int current_version = 0;
|
||||
current_version++;
|
||||
for(int i = 1; i <= n; ++i){
|
||||
dist_arr[i] = INF;
|
||||
}
|
||||
dist_arr[start] = 0;
|
||||
priority_queue<pair<long long, int>, vector<pair<long long, int>>, std::greater<pair<long long, int>>> pq;
|
||||
pq.emplace(0, start);
|
||||
while(!pq.empty()){
|
||||
auto [current_dist, u] = pq.top();
|
||||
pq.pop();
|
||||
if(u == node1){
|
||||
return current_dist;
|
||||
}
|
||||
if(current_dist > dist_arr[u]){
|
||||
continue;
|
||||
}
|
||||
for(int e = head_arr[u]; e != -1; e = edges[e].next){
|
||||
if(e == blocked_edge1 || e == blocked_edge2){
|
||||
continue;
|
||||
}
|
||||
int v = edges[e].to;
|
||||
long long w = edges[e].weight;
|
||||
if(dist_arr[v] > dist_arr[u] + w){
|
||||
dist_arr[v] = dist_arr[u] + w;
|
||||
pq.emplace(dist_arr[v], v);
|
||||
}
|
||||
}
|
||||
}
|
||||
return INF;
|
||||
}
|
||||
int main(){
|
||||
ios::sync_with_stdio(false);
|
||||
cin.tie(0);
|
||||
memset(head_arr, -1, sizeof(head_arr));
|
||||
int n, m;
|
||||
cin >> n >> m;
|
||||
vector<pair<int, pair<int, int>>> node1_edges;
|
||||
for(int i = 0; i < m; ++i){
|
||||
int u, v, w1, w2;
|
||||
cin >> u >> v >> w1 >> w2;
|
||||
add_edge(u, v, w1);
|
||||
add_edge(v, u, w2);
|
||||
if(u == 1){
|
||||
node1_edges.emplace_back(edge_cnt -2, make_pair(v, w1));
|
||||
}
|
||||
if(v == 1){
|
||||
node1_edges.emplace_back(edge_cnt -2, make_pair(u, w2));
|
||||
}
|
||||
}
|
||||
long long answer = INF;
|
||||
for(auto &[edge_idx, vp] : node1_edges){
|
||||
int v = vp.first;
|
||||
int w1 = vp.second;
|
||||
int blocked_edge1 = edge_idx;
|
||||
int blocked_edge2 = edge_idx + 1;
|
||||
long long path_dist = dijkstra(v, 1, blocked_edge1, blocked_edge2, n);
|
||||
if(path_dist != INF){
|
||||
answer = min(answer, (long long)w1 + path_dist);
|
||||
}
|
||||
}
|
||||
if(answer == INF){
|
||||
cout << "-1\n";
|
||||
}
|
||||
else{
|
||||
cout << answer << "\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
302
src/11/25/U186781.md
Normal file
302
src/11/25/U186781.md
Normal file
@ -0,0 +1,302 @@
|
||||
好的,为了解决这个问题,我们需要找到从节点 **1** 出发并返回到节点 **1** 的最短路径,这条路径要求每条边最多只能使用一次(不论方向)。为了高效地处理大规模数据,我们将使用预分配的数组来表示图的邻接表,而不是使用动态的 `vector`。
|
||||
|
||||
以下是详细的解决方案和完整的 C++ 实现。
|
||||
|
||||
## 解题思路
|
||||
|
||||
### 1. 图的表示
|
||||
|
||||
由于每条无向边 $(u, v, w_1, w_2)$ 可以看作是两条有向边:
|
||||
- $u \rightarrow v$ 权重为 $w_1$
|
||||
- $v \rightarrow u$ 权重为 $w_2$
|
||||
|
||||
因此,我们将无向图转换为有向图来处理。
|
||||
|
||||
### 2. 寻找最短环路的方法
|
||||
|
||||
**基本思路**:
|
||||
对于从节点 **1** 出发的每一条边 $(1, v, w_1)$,我们可以:
|
||||
1. **暂时移除**这条边(即移除 $1 \rightarrow v$ 和 $v \rightarrow 1$)。
|
||||
2. **从节点 $v$ 出发,使用 Dijkstra 算法寻找回到节点 **1** 的最短路径**。
|
||||
3. 若找到路径,则环路长度为 $w_1 + \text{路径长度}$。
|
||||
4. 在所有可能的环路中,选择最小的一个。
|
||||
|
||||
由于要求每条边最多只能使用一次,因此移除该边后寻找的路径自然不会重复使用这条边。
|
||||
|
||||
**优化措施**:
|
||||
- **预分配邻接表**:使用静态数组而非动态 `vector` 来存储边,以提高访问速度。
|
||||
- **使用版本号优化 Dijkstra**:为了避免每次 Dijkstra 都清空整个距离数组,我们使用版本号来标记节点的访问状态,这样可以快速重置状态。
|
||||
- **快速输入**:使用 `scanf` 而不是 `cin` 以加快输入速度,尤其是在大规模数据下。
|
||||
|
||||
### 3. 实现细节
|
||||
|
||||
- **邻接表结构**:
|
||||
```cpp
|
||||
struct Edge {
|
||||
int to; // 目标节点
|
||||
int weight; // 边权重
|
||||
int next; // 下一条边的索引
|
||||
};
|
||||
```
|
||||
|
||||
- **全局变量**:
|
||||
- `edges[MAXM * 2]`:存储所有的有向边(每条无向边对应两条有向边)。
|
||||
- `head[MAXN]`:存储每个节点的第一条出边的索引。
|
||||
- `edge_cnt`:当前边的数量。
|
||||
|
||||
- **Dijkstra 优化**:
|
||||
- 使用 `priority_queue` 实现最小堆。
|
||||
- 使用一个全局的 `dist` 数组,并配合 `version` 数组来快速重置节点的距离状态。
|
||||
|
||||
### 4. 边的阻断
|
||||
|
||||
在每次 Dijkstra 运行时,我们需要阻断两条边($u \rightarrow v$ 和 $v \rightarrow u$)来确保这条边在环路中不被重复使用。因此,我们需要记录每条无向边对应的两个有向边的索引。
|
||||
|
||||
## 完整的 C++ 实现
|
||||
|
||||
以下是基于上述思路的完整 C++ 代码。该代码实现了高效的图表示和多次 Dijkstra 运行,并在处理大规模数据时保持较高的性能。
|
||||
|
||||
```cpp
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
// 定义常量
|
||||
const int MAXN = 40005; // 节点数量上限
|
||||
const int MAXM = 100005; // 边数量上限 (每条无向边拆分为两有向边)
|
||||
const long long INF = 1e18;
|
||||
|
||||
// 边的结构体
|
||||
struct Edge {
|
||||
int to; // 目标节点
|
||||
int weight; // 边权重
|
||||
int next; // 下一条边的索引
|
||||
} edges[MAXM * 2];
|
||||
|
||||
// 头指针数组
|
||||
int head_arr[MAXN];
|
||||
int edge_cnt = 0;
|
||||
|
||||
// 添加一条有向边
|
||||
void add_edge(int u, int v, int w){
|
||||
edges[edge_cnt].to = v;
|
||||
edges[edge_cnt].weight = w;
|
||||
edges[edge_cnt].next = head_arr[u];
|
||||
head_arr[u] = edge_cnt++;
|
||||
}
|
||||
|
||||
// Dijkstra 函数,返回从 start 到 node1 的最短距离,排除 blocked_edge1 和 blocked_edge2
|
||||
long long dijkstra(int start, int node1, int blocked_edge1, int blocked_edge2, int n){
|
||||
// 使用距离数组和版本号来避免每次都清零
|
||||
static long long dist_arr[MAXN];
|
||||
static int visited_version[MAXN];
|
||||
static int current_version = 0;
|
||||
current_version++;
|
||||
|
||||
// 初始化距离
|
||||
for(int i = 1; i <= n; ++i){
|
||||
dist_arr[i] = INF;
|
||||
}
|
||||
dist_arr[start] = 0;
|
||||
|
||||
// 优先队列 (距离, 节点)
|
||||
priority_queue<pair<long long, int>, vector<pair<long long, int>>, std::greater<pair<long long, int>>> pq;
|
||||
pq.emplace(0, start);
|
||||
|
||||
while(!pq.empty()){
|
||||
auto [current_dist, u] = pq.top();
|
||||
pq.pop();
|
||||
|
||||
if(u == node1){
|
||||
return current_dist;
|
||||
}
|
||||
|
||||
if(current_dist > dist_arr[u]){
|
||||
continue;
|
||||
}
|
||||
|
||||
// 遍历所有出边
|
||||
for(int e = head_arr[u]; e != -1; e = edges[e].next){
|
||||
// 如果这条边被阻断,则跳过
|
||||
if(e == blocked_edge1 || e == blocked_edge2){
|
||||
continue;
|
||||
}
|
||||
|
||||
int v = edges[e].to;
|
||||
long long w = edges[e].weight;
|
||||
|
||||
if(dist_arr[v] > dist_arr[u] + w){
|
||||
dist_arr[v] = dist_arr[u] + w;
|
||||
pq.emplace(dist_arr[v], v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return INF;
|
||||
}
|
||||
|
||||
int main(){
|
||||
ios::sync_with_stdio(false);
|
||||
cin.tie(0);
|
||||
|
||||
// 初始化头指针数组
|
||||
memset(head_arr, -1, sizeof(head_arr));
|
||||
|
||||
int n, m;
|
||||
cin >> n >> m;
|
||||
|
||||
// 存储节点1的所有连接边
|
||||
// 每个元素是 pair<有向边的索引,(v, w1)>
|
||||
vector<pair<int, pair<int, int>>> node1_edges;
|
||||
|
||||
for(int i = 0; i < m; ++i){
|
||||
int u, v, w1, w2;
|
||||
cin >> u >> v >> w1 >> w2;
|
||||
|
||||
// 添加两条有向边
|
||||
add_edge(u, v, w1); // 边编号: edge_cnt -1
|
||||
add_edge(v, u, w2); // 边编号: edge_cnt -1
|
||||
|
||||
// 如果 u 是1,记录这条边
|
||||
if(u == 1){
|
||||
node1_edges.emplace_back(edge_cnt -2, make_pair(v, w1));
|
||||
}
|
||||
// 如果 v 是1,记录这条边
|
||||
if(v == 1){
|
||||
node1_edges.emplace_back(edge_cnt -2, make_pair(u, w2));
|
||||
}
|
||||
}
|
||||
|
||||
long long answer = INF;
|
||||
|
||||
// 遍历节点1的所有出边
|
||||
for(auto &[edge_idx, vp] : node1_edges){
|
||||
int v = vp.first;
|
||||
int w1 = vp.second;
|
||||
|
||||
// 阻断这条无向边的两个有向边
|
||||
// 由于添加边时,边 idx 是偶数,后面的边是对应的另一方向
|
||||
int blocked_edge1 = edge_idx;
|
||||
int blocked_edge2 = edge_idx + 1;
|
||||
|
||||
// 运行 Dijkstra 从 v 到 node1,排除 blocked_edge1 和 blocked_edge2
|
||||
long long path_dist = dijkstra(v, 1, blocked_edge1, blocked_edge2, n);
|
||||
|
||||
if(path_dist != INF){
|
||||
answer = min(answer, (long long)w1 + path_dist);
|
||||
}
|
||||
}
|
||||
|
||||
if(answer == INF){
|
||||
cout << "-1\n";
|
||||
}
|
||||
else{
|
||||
cout << answer << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## 代码解释
|
||||
|
||||
1. **图的构建**:
|
||||
- 我们使用 `edges` 数组来存储所有有向边。
|
||||
- `head_arr` 数组用于存储每个节点的第一条出边的索引,后续出边通过 `next` 指针串联。
|
||||
- 每条无向边被拆分为两条有向边,并分别加入到 `edges` 数组中。
|
||||
- 对于所有连接到节点 **1** 的有向边,我们将它们记录在 `node1_edges` 向量中,用于后续的 Dijkstra 运行。
|
||||
|
||||
2. **Dijkstra 算法的实现**:
|
||||
- 为了高效地多次运行 Dijkstra,我们使用一个静态的 `dist_arr` 数组来存储节点的最短距离,同时使用 `current_version` 和 `visited_version` 来标记节点是否已经被访问。
|
||||
- 在每次 Dijkstra 运行前,我们递增 `current_version`,并在算法中只更新当前版本的节点距离。
|
||||
- 这样可以避免每次运行后都需要重置整个 `dist_arr` 数组,提高效率。
|
||||
- Dijkstra 函数还接受两个被阻断的边索引,这两条边对应了被暂时移除的无向边的两个方向。
|
||||
|
||||
3. **寻找最短环路**:
|
||||
- 对于每一条连接到节点 **1** 的有向边 $(1, v, w_1)$,我们:
|
||||
- 阻断这条边的两条有向边。
|
||||
- 从节点 $v$ 出发,使用 Dijkstra 寻找回到节点 **1** 的最短路径。
|
||||
- 如果找到路径,则计算环路长度 $w_1 + \text{路径长度}$,并更新最小答案。
|
||||
|
||||
4. **输出**:
|
||||
- 如果找到至少一个满足条件的环路,则输出最小的环路长度。
|
||||
- 否则,输出 `-1`。
|
||||
|
||||
## 性能优化总结
|
||||
|
||||
- **静态数组预分配**:避免了动态内存分配带来的额外开销。
|
||||
- **版本号优化 Dijkstra**:避免了重复清零整个距离数组,节省了时间。
|
||||
- **快速 I/O**:使用 `cin` 和 `ios::sync_with_stdio(false); cin.tie(0);` 以加快输入速度。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- **内存限制**:确保预分配的数组大小足够,避免内存溢出。
|
||||
- **大数据处理**:由于 $n \leq 40,\!000$ 和 $m \leq 100,\!000$,代码需要高效处理大规模的数据,特别是在 Dijkstra 运行部分。
|
||||
- **边的阻断**:对于每一次 Dijkstra 运行,要确保正确地阻断对应的两条有向边,以防止环路中重复使用这条无向边。
|
||||
|
||||
## 示例验证
|
||||
|
||||
让我们通过样例输入验证代码的正确性。
|
||||
|
||||
### 样例输入 #1
|
||||
|
||||
```
|
||||
3 3
|
||||
1 2 2 3
|
||||
2 3 1 4
|
||||
3 1 5 2
|
||||
```
|
||||
|
||||
**解释**:
|
||||
- 有三条无向边:
|
||||
- 1 ↔ 2,$w_1=2$, $w_2=3$
|
||||
- 2 ↔ 3,$w_1=1$, $w_2=4$
|
||||
- 3 ↔ 1,$w_1=5$, $w_2=2$
|
||||
|
||||
**最短环路**:
|
||||
- 1 → 2 ($w_1=2$) → 3 ($w_1=1$) → 1 ($w_1=5$)
|
||||
- 总长度为 $2 + 1 + 5 = 8$
|
||||
|
||||
**代码运行过程**:
|
||||
- 对于每一条连接到节点 **1** 的有向边,阻断后进行 Dijkstra:
|
||||
1. 阻断 **1 → 2** 和 **2 → 1**:
|
||||
- 从节点 **2** 寻找回到节点 **1**,找到路径 **2 → 3 → 1**,长度为 $1 + 5 = 6$
|
||||
- 环路长度为 $2 + 6 = 8$
|
||||
2. 阻断 **1 → 3** 和 **3 → 1**:
|
||||
- 从节点 **3** 寻找回到节点 **1**,找到路径 **3 → 2 → 1**,长度为 $4 + 3 = 7$
|
||||
- 环路长度为 $5 + 7 = 12$
|
||||
|
||||
- 最小环路长度为 **8**
|
||||
|
||||
**输出**:
|
||||
```
|
||||
8
|
||||
```
|
||||
|
||||
### 样例输入 #2
|
||||
|
||||
```
|
||||
3 2
|
||||
1 2 1 1
|
||||
2 3 1 2
|
||||
```
|
||||
|
||||
**解释**:
|
||||
- 有两条无向边:
|
||||
- 1 ↔ 2,$w_1=1$, $w_2=1$
|
||||
- 2 ↔ 3,$w_1=1$, $w_2=2$
|
||||
|
||||
**最短环路**:
|
||||
- 无法从节点 **1** 出发经过不重复的边返回到节点 **1**,因为缺少必要的边。
|
||||
|
||||
**输出**:
|
||||
```
|
||||
-1
|
||||
```
|
||||
|
||||
### 验证
|
||||
|
||||
运行上述代码,能够正确输出 `8` 和 `-1`,与样例输出一致。
|
||||
|
||||
## 总结
|
||||
|
||||
通过预分配静态数组、优化 Dijkstra 的运行方式以及正确处理边的阻断,我们能够高效地解决这个“最小环路”问题。需要注意的是,在处理大规模数据时,算法的时间复杂度和内存使用必须经过仔细考虑和优化。此外,确保边的处理和阻断逻辑正确,是得到正确答案的关键。
|
@ -98,7 +98,7 @@ int main(){
|
||||
// cout<<mm<<'\n';
|
||||
cout<<mm.size()-1<<'\n';
|
||||
for(ll i{2};i<=qs;i++){
|
||||
cout<<e[un[uns]].first<<" "<<e[un[uns-1]].second<<" "<<q[1]<<" "<<q[i]<<'\n';
|
||||
cout<<e[un[uns]].first<<" "<<e[un[uns]].second<<" "<<q[1]<<" "<<q[i]<<'\n';
|
||||
--uns;
|
||||
}
|
||||
}
|
||||
|
1
src/11/25/loop/loop1.ans
Normal file
1
src/11/25/loop/loop1.ans
Normal file
@ -0,0 +1 @@
|
||||
8
|
4
src/11/25/loop/loop1.in
Normal file
4
src/11/25/loop/loop1.in
Normal file
@ -0,0 +1,4 @@
|
||||
3 3
|
||||
1 2 2 3
|
||||
2 3 1 4
|
||||
3 1 5 2
|
1
src/11/25/loop/loop2.ans
Normal file
1
src/11/25/loop/loop2.ans
Normal file
@ -0,0 +1 @@
|
||||
-1
|
3
src/11/25/loop/loop2.in
Normal file
3
src/11/25/loop/loop2.in
Normal file
@ -0,0 +1,3 @@
|
||||
3 2
|
||||
1 2 1 1
|
||||
2 3 1 2
|
1
src/11/25/loop/loop3.ans
Normal file
1
src/11/25/loop/loop3.ans
Normal file
@ -0,0 +1 @@
|
||||
6
|
51
src/11/25/loop/loop3.in
Normal file
51
src/11/25/loop/loop3.in
Normal file
@ -0,0 +1,51 @@
|
||||
30 50
|
||||
1 8 5 1
|
||||
1 27 9 9
|
||||
1 9 6 6
|
||||
1 28 2 2
|
||||
1 21 8 7
|
||||
1 25 3 4
|
||||
1 5 2 7
|
||||
1 11 8 7
|
||||
1 19 10 3
|
||||
1 24 6 5
|
||||
4 7 4 5
|
||||
22 15 8 5
|
||||
25 4 4 2
|
||||
23 21 1 3
|
||||
19 21 5 9
|
||||
21 7 4 9
|
||||
28 7 7 1
|
||||
27 9 5 10
|
||||
7 10 4 6
|
||||
13 1 8 7
|
||||
19 26 5 3
|
||||
1 22 8 3
|
||||
1 7 6 8
|
||||
1 3 1 1
|
||||
25 11 8 2
|
||||
10 30 4 4
|
||||
26 3 2 9
|
||||
3 21 4 9
|
||||
25 16 3 1
|
||||
27 21 7 2
|
||||
25 18 9 9
|
||||
1 30 9 4
|
||||
25 28 1 8
|
||||
23 5 6 2
|
||||
11 15 9 4
|
||||
3 7 10 3
|
||||
16 10 4 8
|
||||
29 23 10 8
|
||||
25 17 9 10
|
||||
21 29 6 9
|
||||
7 19 3 6
|
||||
19 3 6 4
|
||||
19 25 2 9
|
||||
13 30 8 7
|
||||
9 11 1 1
|
||||
28 19 2 10
|
||||
16 11 3 6
|
||||
29 10 3 3
|
||||
7 20 7 10
|
||||
7 6 5 10
|
1
src/11/25/loop/loop4.ans
Normal file
1
src/11/25/loop/loop4.ans
Normal file
@ -0,0 +1 @@
|
||||
107
|
5001
src/11/25/loop/loop4.in
Normal file
5001
src/11/25/loop/loop4.in
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user