分区飞盘 C++

Posted

技术标签:

【中文标题】分区飞盘 C++【英文标题】:Partition Frisbees C++ 【发布时间】:2015-03-17 04:42:20 【问题描述】:

我们有一组 F 的 n 个 2D 飞盘。我们希望将 F 划分为两个子集 F1 和 F2,以便在每个子集中没有两个飞盘相交。我们的函数接受这样的输入:(x_j, y_j) 是第 j 个飞盘的中心,rad_j 是第 j 个飞盘的半径。输出应该是 s_0 s_1 ... s_n-1,如果第 j 个飞盘在 F1 中,则 s_j = 1;如果第 j 个飞盘在 F2 中,则 s_i = 2。如果不能对 F 进行分区,则返回 0。理想情况下,算法应该在 O(n^2) 时间内计算。

我想我应该使用某种类型的矩阵表示,比如图,但是我认为我不需要构建一个图,但我认为我 BFS/DFS 会很有用,但我坚持如何在 O(n^2) 中优雅地做到这一点。顺便说一句,我正在用 C++ 编写代码。

【问题讨论】:

【参考方案1】:

您在图形搜索方面走在了正确的轨道上。这是一个使用 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!

【讨论】:

【参考方案2】:

你不应该只做一个堆叠的for循环并使用距离公式吗?即如果它们的中心之间的距离小于它们的半径之和,则两个相交。

之后,您已经对其进行状态分解,然后您可以继续执行循环包含/排除(即包含所有内容并删除所有无效的内容,然后包含尽可能多的有效内容等)

【讨论】:

我还是不明白。你说的状态爆炸是什么意思?我有这个作为面试问题,但我搞砸了。所以现在我只是想自己编写代码。你有实现吗?【参考方案3】:

您可以构建一个图,其中边表示“接触”。然后您可以在该图上使用二分算法。 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),因为您必须检查每个圆盘与所有其他圆盘,尽管您可以通过构建地理四叉树首先对圆盘进行排序来优化常见情况。

【讨论】:

以上是关于分区飞盘 C++的主要内容,如果未能解决你的问题,请参考以下文章

在不循环 c++ 的情况下对 2D 数组进行分区

C++内存分区模型

C++内存分区模型

C++核心编程内存分区模型

c++中的分区函数

C++:四大内存分区