问题描述:
给定直线上
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;
}