【贪心法】黑白连线问题

问题描述:
给定直线上 2 n 2n 2n 个点的序列 P [ 1 , 2 , … , 2 n ] P[1,2,… ,2n] P[1,2,,2n],每个点 P [ i ] P[i] P[i] 要么是白点(用数字 1 1 1 表示)要么是黑点(用数字 0 0 0 表示),其中共有 n n n 个白点和 n n n 个黑点,相邻两个点之间距离均为 1 1 1,请设计一个算法将每个白点与一黑点相连,使得连线的总长度最小。

黑白连线问题示例答案
例如,图中有 4 4 4 个白点和 4 4 4 个黑点,以图中方式相连,连线总长度为 1 + 1 + 1 + 5 = 8 1+1+1+5=8 1+1+1+5=8


贪心法:

思路1:
每遇到一个未被连接点,就向后寻找第一个(最近的)不同的点。
这个思路时间复杂度较高、空间复杂度较低。

思路2:
(1)维护一个黑点栈和白点栈;
(2)按顺序遍历每一个点,如果遇到一个白点,就查看当前黑点栈是否为空,非空的话就将将该白点与黑点栈顶黑点连接(因为栈是先入先出,所以栈顶黑点就是离该白点最近的未连接的黑点);遇到黑点也做类似的操作。
这个思路时间复杂度较低、空间复杂度较高。


思路2的C++实现:

//
//  main.cpp
//  ConnectBlackAndWhitePoint
//
//  Created by 胡昱 on 2021/12/31.
//

#include <iostream>
#include <stack>
using namespace std;

int main(int argc, const char * argv[]) {
    // 共m组测试数据
    int m;
    cin >> m;
    while((m--) > 0) {
        // 输入黑白点的数量n,即共有2*n个点
        int n;
        cin >> n;
        
        // 创建点数组points并输入点
        int* points = new int[2*n];
        for(int pi = 0; pi < 2 * n; ++pi) {
            cin >> points[pi];
        }
        
        // 创建黑白点栈
        stack<int> whitePoints;
        stack<int> blackPoints;
        
        // 初始化结果
        int result = 0;
        
        // 开始贪心算法
        for(int pi = 0; pi < 2 * n; ++pi) {
            // 如果是白色的点
            if(points[pi] == 0) {
                // 如果没有黑点待分配,就将这个白点入栈,等一个最近的黑点来挑它
                if(blackPoints.empty()) {
                    whitePoints.push(pi);
                }
                // 如果黑点栈中有未分配的黑点,就选择栈顶元素(也就是最近的黑点)
                else {
                    result += (pi - blackPoints.top());
                    blackPoints.pop();
                }
            }
            // 如果是黑色的点
            else {
                // 如果没有白点待分配,就将这个黑点入栈,等一个最近的白点来挑它
                if(whitePoints.empty()) {
                    blackPoints.push(pi);
                }
                // 如果白点栈中有未分配的白点,就选择栈顶元素(也就是最近的白点)
                else {
                    result += (pi - whitePoints.top());
                    whitePoints.pop();
                }
            }
        }
        
        // 输出结果并释放资源
        cout << result << endl;
        delete [] points;
    }
    return 0;
}

### 黑白点对之间的最小生成树算法或最小总连线长度计算方法 要解决黑白点之间最小总连线长度的问题,可以考虑使用 **Kruskal 算法** 或者 **Prim 算法** 来构建最小生成树(MST, Minimum Spanning Tree)。这两种经典图论算法能够有效地找到连接所有节点的最短路径集合。 #### Kruskal 算法 Kruskal 算法是一种基于边权重排序的方法。其核心思想是对所有的边按照权值从小到大进行排序,并依次选取不形成环路的边加入生成树中,直到所有顶点都被连通为止[^3]。 以下是该算法的一个 Python 实现: ```python def kruskal(edges, n_points): parent = list(range(n_points)) def find(u): while parent[u] != u: parent[u] = parent[parent[u]] u = parent[u] return u edges.sort(key=lambda edge: edge[2]) # Sort by weight mst_weight = 0 mst_edges = [] for u, v, w in edges: root_u = find(u) root_v = find(v) if root_u != root_v: parent[root_v] = root_u mst_weight += w mst_edges.append((u, v, w)) return mst_weight, mst_edges ``` 上述代码中的 `edges` 是一个三元组列表 `(u, v, w)` 表示从点 `u` 到点 `v` 的边具有权重 `w`。通过调用此函数即可获得最小生成树及其总重量。 #### Prim 算法 另一种常用的选择是 Prim 算法,它更适合稠密图的情况。Prim 算法维护了一个优先队列来动态更新当前未访问节点中最优的一条边[^4]。 下面是 Prim 算法的一种实现方式: ```python import heapq def prim(graph, start=0): visited = set() heap = [(0, start)] # (weight, node) total_cost = 0 mst_edges = [] while heap and len(visited) < len(graph): cost, current_node = heapq.heappop(heap) if current_node not in visited: visited.add(current_node) total_cost += cost for neighbor, weight in graph[current_node]: if neighbor not in visited: heapq.heappush(heap, (weight, neighbor)) if cost > 0: # Avoid adding the initial dummy edge. mst_edges.append((current_node, neighbor, weight)) return total_cost, mst_edges ``` 这里假设输入是一个邻接表形式表示的无向加权图 `graph`,其中每个键对应于某个顶点,而对应的值则是一系列二元组 `(neighbor, weight)` 描述相邻结点以及它们间的距离。 无论是采用哪种具体技术路线,在实际应用过程中都需要先定义好两点间欧几里得距离作为边权值的基础公式\[d=\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}\][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值