[英]Acyclic undirected graph allocation problem
我们有一个分配问题:每个节点都是一个燃料源,每条边包含许多均匀间隔的灯(将它们视为彼此相距 1 m)。 每个燃料源都可以为紧靠其周围边缘的灯供电(它不能通过其他节点为灯供电)。 燃料源也有一个半径,它可以为周围的灯提供燃料——根据半径(以米为单位),我们可以计算给定燃料源提供的燃料量——例如:半径为 2 的燃料源可以为所有灯提供燃料。其半径内的灯,总共消耗 2 升燃料(燃料使用量为半径的 function)。
请注意,两个节点之间只有一条路径(意味着边数等于节点数 - 1)。
目标是计算燃料源的最佳半径,以便我们在为所有灯加燃料时最大限度地减少燃料使用量。
该图的解决方案如下所示。 红色椭圆体应该可视化燃料源的半径,下面是相关值的表格:
节点、燃料源 | 半径、油耗 |
---|---|
0 | 1个 |
1个 | 2个 |
2个 | 0 |
3个 | 2个 |
4个 | 0 |
5个 | 0 |
将所有燃料量加起来后,我们得到结果: 5 。
上述任务的输入如下所示:
6 // 6 nodes (numVertices)
0 1 3 // Node 0 with an edge containing 3 nodes going to node 3 (src dest lights)
1 2 1 // ...
2 3 2
1 4 2
1 5 2
到目前为止,我已经尝试像这样加载我的边缘(请注意,这个解决方案非常糟糕,尤其是我使用指针时):
struct light {
int count;
};
struct node {
int d;
light* l;
};
std::vector<node*>* tree;
int numVertices;
// Do this for all values in the input
void AddEdge(int src, int dest, int lights) {
light* l = new light{ lights };
tree[src].push_back(new node{ dest, l });
tree[dest].push_back(new node{ src, l });
}
然后我通过使用贪婪算法每一步“移除”尽可能多的灯来解决这个问题:
void Solve() {
int fuel = 0;
while (true) {
int maxNode = 0;
int maxNodeLights = 0;
for (int A = 0; A < numVertices; A++)
{
int lightsOnNode = 0;
for (node* B : tree[A])
{
lightsOnNode += B->l->count;
}
if (lightsOnNode > maxNodeLights) {
maxNodeLights = lightsOnNode;
maxNode = A;
}
}
if (maxNodeLights > 0) {
bool addedRange = false;
for (node* B : tree[maxNode])
{
if (B->l->count > 0) {
B->l->count--;
addedRange = true;
}
}
if (addedRange) {
fuel++;
}
}
else {
break;
}
}
std::cout << fuel << '\n';
}
如果我们要解析示例案例中的输入字符串,它将如下所示:
numVertices = 6;
AddEdge(0, 1, 3);
AddEdge(1, 2, 1);
AddEdge(2, 3, 2);
AddEdge(1, 4, 2);
AddEdge(1, 5, 2);
Solve();
这适用于像上面那样的简单图形,但一旦引入更复杂的图形,它就会以小幅度失败,因为如果未来有几个更好的选择,贪婪算法不会向前看。
可以在此处找到一个较长的测试用例。 该图消耗的燃料量为 77481。
新的、失败的测试用例:
1 0 4
2 1 1
3 2 4
4 3 1
5 1 2
6 1 1
7 2 2
8 3 2
9 1 1
10 1 3
11 5 1
12 0 2
13 10 4
14 3 3
15 5 4
(输出:16。正确的 output:17)
1 0 2
2 1 3
3 2 2
4 1 4
5 4 3
6 3 2
7 5 3
8 3 4
(输出:10。正确的 output:11)
1 0 4
2 0 3
3 0 4
4 3 3
5 2 2
6 3 1
7 2 1
8 3 2
9 3 2
10 2 1
11 9 1
12 4 2
13 5 2
14 8 2
15 9 1
16 14 2
17 3 3
18 3 4
(输出:15。正确的 output:16)
算法伪代码:
C++ 代码位于https://github.com/JamesBremner/LampLighter 。
Input
0 1 1
1 2 1
2 3 1
3 4 1
0 5 1
5 6 1
6 7 1
7 8 1
0 9 1
9 10 1
10 11 1
11 12 1
Source radii
0 r=0
1 r=1
2 r=0
3 r=1
4 r=0
5 r=1
6 r=0
7 r=1
8 r=0
9 r=1
10 r=0
11 r=1
12 r=0
Total fuel 6
0 1 3
1 2 1
0 5 3
5 6 1
Source radius
0 r=2
1 r=1
2 r=0
5 r=1
6 r=0
Total fuel 4
lamp ..\test\data11_19_2.txt
Output:
1 0 2
2 1 3
3 2 2
4 1 4
5 4 3
6 3 2
7 5 3
8 3 4
Source radius
1 r=3
0 r=0
2 r=0
3 r=4
4 r=0
5 r=3
6 r=0
7 r=0
8 r=0
Total fuel 10
致谢:该算法基于MarkB的见解,他建议加油应从叶节点开始并“向下”工作
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.