求所有pair

  • 假设有1063个类, 每个类有两个元素p1,p2,求出所有的pair

思路1:类间组合

算法思维描述: 每个类只与其后面的类组合,每次类间组合产生1个正样本对,N*4个负样本对(4对应 p1q1,p1q2,p2q1,p2q2
第1个类:1(正样本对)+1062*4(负样本对)
第2个类:1(正样本对)+1061*4(负样本对)

第1062个类:1(正样本对)+1*4(负样本对)
第1063个类: 1(正样本对)
=>1+1062*4 + 1+1061*4 + …1+1*4 + 1
= 1063 + (1062+1061+1060 + …+1)*4
= 1063 + (1062+1)*(1062/2)*4
=1063+1063*1062*2
=1063*(1+1062*2)

排列组合描述:
样本对=正样本对+负样本对
=1063(正) + (1062+1061+1060 + …+1)*4

思路2:直接考虑元素间的组合

算法思维描述:
1063*2个元素,每个元素只与其后面的元素组合成pair
第1个元素:1063*2-1
第2个元素:1063*2-2

第1063*2-1个元素:1
(1063*2-1) + (1063*2-2) + … + 1
-> [(1063*2-1) + 1] * [ (1063*2-1) /2 ]
->1063*2*[ (1063*2-1) /2 ]
->1063*(1063*2-1)->1063*(1062*2+1)

排列组合描述:
C21063=1063×2×(1063×21)2

<think>题目要使用C++,并且不使用vector和pair。我们可以使用数组和结构体来构建邻接表,并使用SPFA算法(题目中提到了SPFA)来解决。但是题目中要使用SPFA算法,并且不用vector和pair,所以我们需要手动实现邻接表。 思路: 1. 构建两个图:原图和反向图(用于计算从各点到x的最短距离,即原图中从x到各点反向图的最短距离就是原图各点到x的最短距离)。 2. 使用SPFA算法分别计算: - 从x到其他各点的最短距离(回程,在原图上计算) - 从其他各点到x的最短距离(去程,在反向图上计算,相当于原图各点到x的距离) 3. 对于每头牛,去程+回程的距离,取最大值。 注意:题目中可能有重边,我们使用邻接表存储,重边都会存储下来,在SPFA中会自然处理。 邻接表实现: - 使用结构体数组edge存储边,包括to(目标节点)、weight(边权)、next(下一条边的索引) - 使用head数组,head[u]表示节点u的第一条边在edge数组中的位置 步骤: - 初始化图:初始化head数组为-1(表示没有边),edge数组索引从0开始 - 加边函数:将新边加入edge数组,并更新head数组 由于边数最大为100000,所以需要开足够大的数组。 我们将原图称为G,反向图称为revG。 分别对G和revG进行SPFA,得到两个距离数组: dist1[i]: 在原图G上,从x到i的最短距离(回程) dist2[i]: 在反向图revG上,从x到i的最短距离(即原图中从i到x的最短距离,去程) 然后,对于每头牛i,计算dist1[i] + dist2[i]的最大值。 注意:SPFA需要队列,我们使用数组模拟循环队列。 代码结构: 1. 定义常量:最大节点数、最大边数、无穷大 2. 定义结构体Edge 3. 定义全局数组:head, rev_head, edge, rev_edge, dist1, dist2 4. 加边函数(原图和反向图同时加) 5. SPFA函数:传入图(head和edge数组),起点,距离数组 6. 主函数:读入数据,建图,运行两次SPFA(一次原图,一次反向图),计算结果 由于题目中节点数N<=1000,边数M<=100000,所以我们可以开数组。 具体实现:</think># 使用数组和结构体实现 SPFA 算法解决农场派对问题 以下是使用 C++ 实现 SPFA 算法解决农场派对问题的方案,完全避免使用 `vector` 和 `pair`,采用数组和结构体实现邻接表。 ```cpp #include <iostream> #include <cstring> #include <queue> #include <climits> using namespace std; const int MAX_N = 1005; // 最大节点数 const int MAX_M = 100005; // 最大边数 const int INF = INT_MAX; // 无穷大值 // 边结构体 struct Edge { int to; // 目标节点 int weight; // 边权重 int next; // 下一条边的索引 }; Edge graph_edges[MAX_M]; // 原图边数组 int graph_head[MAX_N]; // 原图头指针数组 int graph_edge_count = 0; // 原图边计数器 Edge rev_graph_edges[MAX_M]; // 反向图边数组 int rev_graph_head[MAX_N]; // 反向图头指针数组 int rev_graph_edge_count = 0;// 反向图边计数器 // 添加边到原图 void add_graph_edge(int from, int to, int weight) { graph_edges[graph_edge_count] = {to, weight, graph_head[from]}; graph_head[from] = graph_edge_count++; } // 添加边到反向图 void add_rev_graph_edge(int from, int to, int weight) { rev_graph_edges[rev_graph_edge_count] = {to, weight, rev_graph_head[from]}; rev_graph_head[from] = rev_graph_edge_count++; } // SPFA 算法实现 void spfa(Edge edges[], int head[], int dist[], int start, int n) { // 初始化距离数组 for (int i = 1; i <= n; i++) { dist[i] = INF; } dist[start] = 0; // 使用队列存储待处理节点 queue<int> q; q.push(start); // 节点是否在队列中 bool in_queue[MAX_N] = {false}; in_queue[start] = true; while (!q.empty()) { int u = q.front(); q.pop(); in_queue[u] = false; // 遍历所有邻接边 for (int i = head[u]; i != -1; i = edges[i].next) { int v = edges[i].to; int w = edges[i].weight; // 松弛操作 if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; if (!in_queue[v]) { q.push(v); in_queue[v] = true; } } } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m, x; cin >> n >> m >> x; // 初始化头指针数组 memset(graph_head, -1, sizeof(graph_head)); memset(rev_graph_head, -1, sizeof(rev_graph_head)); // 读取边并构建图和反向图 for (int i = 0; i < m; i++) { int a, b, t; cin >> a >> b >> t; add_graph_edge(a, b, t); // 原图:a->b add_rev_graph_edge(b, a, t); // 反向图:b->a(相当于原图的逆边) } // 距离数组 int dist_to[MAX_N]; // 从各点到x的距离 int dist_back[MAX_N]; // 从x到各点的距离 // 计算去程距离(从各点到x,使用反向图) spfa(rev_graph_edges, rev_graph_head, dist_to, x, n); // 计算回程距离(从x到各点,使用原图) spfa(graph_edges, graph_head, dist_back, x, n); // 最长总路径 int max_path = 0; for (int i = 1; i <= n; i++) { // 跳过不可达节点 if (dist_to[i] == INF || dist_back[i] == INF) continue; int total = dist_to[i] + dist_back[i]; if (total > max_path) { max_path = total; } } cout << max_path << endl; return 0; } ``` ## 代码详细解释 ### 1. 数据结构设计 ```cpp struct Edge { int to; // 目标节点 int weight; // 边权重 int next; // 下一条边的索引 }; ``` - `to`: 边指向的目标节点 - `weight`: 边的权重 - `next`: 存储下一条边的索引(链式存储结构) ### 2. 邻接表实现 ```cpp Edge graph_edges[MAX_M]; // 原图边数组 int graph_head[MAX_N]; // 原图头指针数组 int graph_edge_count = 0; // 原图边计数器 Edge rev_graph_edges[MAX_M]; // 反向图边数组 int rev_graph_head[MAX_N]; // 反向图头指针数组 int rev_graph_edge_count = 0;// 反向图边计数器 ``` - `graph_head[u]` 存储节点 u 的第一条边在 `graph_edges` 中的索引 - 通过 `next` 指针形成链表,遍历节点 u 的所有邻接边 - 反向图用于计算从各点到派对农场 x 的距离 ### 3. 添加边的函数 ```cpp void add_graph_edge(int from, int to, int weight) { graph_edges[graph_edge_count] = {to, weight, graph_head[from]}; graph_head[from] = graph_edge_count++; } ``` 1. 创建新边:目标节点 `to`,权重 `weight` 2. 新边的 `next` 指向当前 `head[from]` 3. 更新 `head[from]` 指向新边的索引 4. 边计数器递增 ### 4. SPFA 算法实现 ```cpp void spfa(Edge edges[], int head[], int dist[], int start, int n) { // 初始化距离数组 for (int i = 1; i <= n; i++) { dist[i] = INF; } dist[start] = 0; queue<int> q; q.push(start); bool in_queue[MAX_N] = {false}; in_queue[start] = true; while (!q.empty()) { int u = q.front(); q.pop(); in_queue[u] = false; // 遍历所有邻接边 for (int i = head[u]; i != -1; i = edges[i].next) { int v = edges[i].to; int w = edges[i].weight; // 松弛操作 if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; if (!in_queue[v]) { q.push(v); in_queue[v] = true; } } } } } ``` 1. **初始化**:距离数组设为无穷大,起点距离为 0 2. **队列初始化**:起点加入队列 3. **循环处理**: - 取出队列头部节点 u - 遍历 u 的所有邻接边 - 对每条边执行松弛操作:如果通过 u 到达 v 的距离更短,则更新距离 - 如果 v 不在队列中,则加入队列 4. **结束条件**:队列为空 ### 5. 主函数逻辑 ```cpp int main() { // 初始化 memset(graph_head, -1, sizeof(graph_head)); memset(rev_graph_head, -1, sizeof(rev_graph_head)); // 构建图和反向图 for (int i = 0; i < m; i++) { add_graph_edge(a, b, t); // 原图:a->b add_rev_graph_edge(b, a, t); // 反向图:b->a } // 计算距离 spfa(rev_graph_edges, rev_graph_head, dist_to, x, n); // 去程距离 spfa(graph_edges, graph_head, dist_back, x, n); // 回程距离 // 计算最长路径 int max_path = 0; for (int i = 1; i <= n; i++) { if (dist_to[i] != INF && dist_back[i] != INF) { max_path = max(max_path, dist_to[i] + dist_back[i]); } } } ``` 1. **初始化**:头指针数组设为 -1(表示空链表) 2. **构建图**: - 原图存储正常方向边 - 反向图存储反向边(用于计算从各点到 x 的距离) 3. **计算距离**: - 在反向图上计算从 x 到各点的距离(即原图中各点到 x 的距离) - 在原图上计算从 x 到各点的距离(回程距离) 4. **计算结果**:遍历所有节点,计算最大往返距离 ## 算法分析 1. **时间复杂度**: - SPFA 平均时间复杂度 O(kM),其中 k 是常数 - 最坏情况 O(NM),但实际应用中通常表现良好 - 对于稀疏图,SPFA 效率优于 Dijkstra 2. **空间复杂度**: - 邻接表存储:O(N + M) - 距离数组:O(N) - 队列:O(N) 3. **反向图技巧**: - 在反向图上从 x 运行 SPFA,得到的是原图中各点到 x 的最短距离 - 避免了对每个节点运行 SPFA 的 O(NM) 复杂度 4. **重边处理**: - 邻接表自然支持重边,SPFA 会自动选择最短路径
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值