隔断飞盘C++
Partition Frisbees C++
我们有一组 F 的 n 个 2D 飞盘。我们希望将 F 分为两个子集 F1 和 F2,这样每个子集中就不会有两个飞盘相交。我们的函数接收输入如下:(x_j,y_j)是j-th飞盘的中心,rad_j是j-th飞盘的半径。输出应s_0 s_1...s_n-1,其中 s_j = 1 如果 j 飞盘在 F1 中,s_i = 2,如果 j 飞盘在 F2 中。如果无法对 F 进行分区,则只需返回 0。理想情况下,算法应该在 O(n^2) 时间内计算。
我想我应该使用某种类型的矩阵表示,比如图,但我认为我不需要构建一个图,但我认为 I BFS/DFS 会很有用,但我坚持如何在 O(n^2) 中优雅地做到这一点。顺便说一下,我正在用C++编码。
你在图形搜索方面走在正确的轨道上。 下面是使用 O(V+E) 空间的 C++11, O(V^2) 深度优先搜索解决方案。
DFS 本身在时间上是 O(V+E),但生成邻接列表是 O(V^2) 的明显方式。
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
struct Frisbee
{
double x;
double y;
double radius;
};
int dfs(const vector< vector<int> > &adj, vector<int> &p, int last_ind, int curr_ind)
{
if (p[curr_ind]) // node already painted
{
if (p[last_ind] == p[curr_ind]) // painted same color as neighbor -> failure
return 0;
return 1; // painting is compatible
}
// node not yet painted
p[curr_ind] = (1 == p[last_ind] ? 2 : 1); // paint opposite color as neighbor
for (int j = 0; j < adj[curr_ind].size(); ++j)
if (!dfs(adj, p, curr_ind, adj[curr_ind][j])) // dfs on neighbors
return 0;
return 1;
}
int partition(const vector<Frisbee> &F, vector<int> &p)
{
// compute adjacency lists
vector< vector<int> > adj(F.size());
p.resize(F.size());
for (int i = 0; i < F.size(); ++i)
{
p[i] = 0;
for (int j = i + 1; j < F.size(); ++j)
{
double dist = sqrt((F[i].x - F[j].x) * (F[i].x - F[j].x) + (F[i].y - F[j].y) * (F[i].y - F[j].y));
if (dist < F[i].radius + F[j].radius)
{
adj[i].push_back(j);
adj[j].push_back(i);
}
}
}
// find starting points for dfs
for (int i = 0; i < F.size(); ++i)
if (0 == p[i]) // node i not yet painted
{
p[i] = 1; // arbitrarily choose initial color
for (int j = 0; j < adj[i].size(); ++j)
if (!dfs(adj, p, i, adj[i][j])) // dfs on neighbors
return 0;
}
return 1;
}
int main(int argc, char **argv)
{
vector<Frisbee> F = { { 1.0, 1.0, 1.0 }, { 2.0, 2.0, 1.0 }, { -1.0, -1.0, 1.0 }, { -2.0, -2.0, 1.0 }, { 5.0, 5.0, 1.0 }, { -5.0, 5.0, 1.0 } };
vector<int> p;
if (partition(F, p))
{
for (size_t i = 0; i < F.size(); ++i)
cout << p[i] << " ";
cout << endl;
}
else
cout << "No partition possible!" << endl;
F.push_back({ 1.5, 1.5, 1.0 }); // add a 3-way intersection
if (partition(F, p))
{
for (size_t i = 0; i < F.size(); ++i)
cout << p[i] << " ";
cout << endl;
}
else
cout << "No partition possible!" << endl;
return 0;
}
这是输出(飞盘组上两个分区的输出):
1 2 1 2 1 1
No partition possible!
你不应该只做一个堆叠的for循环并使用距离公式吗? 即,如果两个中心之间的距离小于它们的半径之和,则它们相交。
之后,您已经将其状态分解,然后您可以继续执行循环包含/排除(即包含所有内容并删除所有无效内容,然后包含尽可能多的有效内容等)。
您可以构建一个图形,其中边缘表示"触摸"。然后,您可以在该图上使用双分区算法。Boost.Graph包含一个。
http://www.boost.org/doc/libs/1_57_0/libs/graph/doc/is_bipartite.html
算法是 O(V+E),即如果所有光盘相互接触,则最坏情况为 O(V^2)(尽管在这种情况下它很有可能会提前中止)。
天真地构建图形是 O(V^2),因为您必须检查每个光盘与所有其他光盘,尽管您可以通过构建地理四叉树来首先对光盘进行排序来优化常见情况。
- 没有找到相关文章